blob: 53d894e3eaeb777cf7c428075dce134740068459 [file] [log] [blame]
Duy Truongf3ac7b32013-02-13 01:07:28 -08001/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
Shashank Mittal024c0332010-02-03 11:44:00 -08002
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.
Duy Truongf3ac7b32013-02-13 01:07:28 -080012 * * Neither the name of The Linux Foundation nor the names of its
Shashank Mittal024c0332010-02-03 11:44:00 -080013 * 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>
Greg Griscod6250552011-06-29 14:40:23 -070039#include <platform.h>
Kinson Chikf1a43512011-07-14 11:28:39 -070040#include <partition_parser.h>
Greg Griscod2471ef2011-07-14 13:00:42 -070041#include <mmc.h>
Shashank Mittal024c0332010-02-03 11:44:00 -080042
43#include "recovery.h"
44#include "bootimg.h"
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -070045#include "smem.h"
46
47#define BOOT_FLAGS 1
48#define UPDATE_STATUS 2
49#define ROUND_TO_PAGE(x,y) (((x) + (y)) & (~(y)))
Shashank Mittal024c0332010-02-03 11:44:00 -080050
51static const int MISC_PAGES = 3; // number of pages to save
52static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
53static char buf[4096];
54unsigned boot_into_recovery = 0;
55
Shashank Mittal162244e2011-08-08 19:01:25 -070056extern void reset_device_info();
57extern void set_device_root();
Shashank Mittal024c0332010-02-03 11:44:00 -080058
59int get_recovery_message(struct recovery_message *out)
60{
61 struct ptentry *ptn;
62 struct ptable *ptable;
63 unsigned offset = 0;
64 unsigned pagesize = flash_page_size();
65
66 ptable = flash_get_ptable();
67
68 if (ptable == NULL) {
69 dprintf(CRITICAL, "ERROR: Partition table not found\n");
70 return -1;
71 }
72 ptn = ptable_find(ptable, "misc");
73
74 if (ptn == NULL) {
75 dprintf(CRITICAL, "ERROR: No misc partition found\n");
76 return -1;
77 }
78
79 offset += (pagesize * MISC_COMMAND_PAGE);
Greg Griscod6250552011-06-29 14:40:23 -070080 if (flash_read(ptn, offset, (void *) buf, pagesize)) {
Shashank Mittal024c0332010-02-03 11:44:00 -080081 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
82 return -1;
83 }
84 memcpy(out, buf, sizeof(*out));
85 return 0;
86}
87
88int set_recovery_message(const struct recovery_message *in)
89{
90 struct ptentry *ptn;
91 struct ptable *ptable;
92 unsigned offset = 0;
93 unsigned pagesize = flash_page_size();
94 unsigned n = 0;
95
96 ptable = flash_get_ptable();
97
98 if (ptable == NULL) {
99 dprintf(CRITICAL, "ERROR: Partition table not found\n");
100 return -1;
101 }
102 ptn = ptable_find(ptable, "misc");
103
104 if (ptn == NULL) {
105 dprintf(CRITICAL, "ERROR: No misc partition found\n");
106 return -1;
107 }
108
109 n = pagesize * (MISC_COMMAND_PAGE + 1);
110
Greg Griscod6250552011-06-29 14:40:23 -0700111 if (flash_read(ptn, offset, (void *) SCRATCH_ADDR, n)) {
Shashank Mittal024c0332010-02-03 11:44:00 -0800112 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
113 return -1;
114 }
115
116 offset += (pagesize * MISC_COMMAND_PAGE);
117 offset += SCRATCH_ADDR;
Greg Griscod6250552011-06-29 14:40:23 -0700118 memcpy((void *) offset, in, sizeof(*in));
Shashank Mittal024c0332010-02-03 11:44:00 -0800119 if (flash_write(ptn, 0, (void *)SCRATCH_ADDR, n)) {
120 dprintf(CRITICAL, "ERROR: flash write fail!\n");
121 return -1;
122 }
Greg Griscod6250552011-06-29 14:40:23 -0700123 return 0;
Shashank Mittal024c0332010-02-03 11:44:00 -0800124}
125
126int read_update_header_for_bootloader(struct update_header *header)
127{
128 struct ptentry *ptn;
129 struct ptable *ptable;
130 unsigned offset = 0;
131 unsigned pagesize = flash_page_size();
132
133 ptable = flash_get_ptable();
134 if (ptable == NULL) {
135 dprintf(CRITICAL, "ERROR: Partition table not found\n");
136 return -1;
137 }
138 ptn = ptable_find(ptable, "cache");
139
140 if (ptn == NULL) {
141 dprintf(CRITICAL, "ERROR: No cache partition found\n");
142 return -1;
143 }
144 if (flash_read(ptn, offset, buf, pagesize)) {
145 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
146 return -1;
147 }
148 memcpy(header, buf, sizeof(*header));
149
Greg Griscod6250552011-06-29 14:40:23 -0700150 if (strncmp((char *) header->MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE))
Shashank Mittal024c0332010-02-03 11:44:00 -0800151 {
152 return -1;
153 }
154 return 0;
155}
156
157int update_firmware_image (struct update_header *header, char *name)
158{
159 struct ptentry *ptn;
160 struct ptable *ptable;
161 unsigned offset = 0;
162 unsigned pagesize = flash_page_size();
163 unsigned pagemask = pagesize -1;
164 unsigned n = 0;
165
166 ptable = flash_get_ptable();
167 if (ptable == NULL) {
168 dprintf(CRITICAL, "ERROR: Partition table not found\n");
169 return -1;
170 }
171
172 ptn = ptable_find(ptable, "cache");
173 if (ptn == NULL) {
174 dprintf(CRITICAL, "ERROR: No cache partition found\n");
175 return -1;
176 }
177
178 offset += header->image_offset;
179 n = (header->image_length + pagemask) & (~pagemask);
180
Greg Griscod6250552011-06-29 14:40:23 -0700181 if (flash_read(ptn, offset, (void *) SCRATCH_ADDR, n)) {
Shashank Mittal024c0332010-02-03 11:44:00 -0800182 dprintf(CRITICAL, "ERROR: Cannot read radio image\n");
183 return -1;
184 }
185
186 ptn = ptable_find(ptable, name);
187 if (ptn == NULL) {
188 dprintf(CRITICAL, "ERROR: No %s partition found\n", name);
189 return -1;
190 }
191
Greg Griscod6250552011-06-29 14:40:23 -0700192 if (flash_write(ptn, 0, (void *) SCRATCH_ADDR, n)) {
Shashank Mittal024c0332010-02-03 11:44:00 -0800193 dprintf(CRITICAL, "ERROR: flash write fail!\n");
194 return -1;
195 }
196
197 dprintf(INFO, "Partition writen successfully!");
198 return 0;
199}
200
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700201static int set_ssd_radio_update (char *name)
202{
203 struct ptentry *ptn;
204 struct ptable *ptable;
205 unsigned int ssd_cookie[2] = {0x53534443, 0x4F4F4B49};
206 unsigned pagesize = flash_page_size();
207 unsigned pagemask = pagesize -1;
208 unsigned n = 0;
209
210 ptable = flash_get_ptable();
211 if (ptable == NULL) {
212 dprintf(CRITICAL, "ERROR: Partition table not found\n");
213 return -1;
214 }
215
216 n = (sizeof(ssd_cookie) + pagemask) & (~pagemask);
217
218 ptn = ptable_find(ptable, name);
219 if (ptn == NULL) {
220 dprintf(CRITICAL, "ERROR: No %s partition found\n", name);
221 return -1;
222 }
223
224 if (flash_write(ptn, 0, ssd_cookie, n)) {
225 dprintf(CRITICAL, "ERROR: flash write fail!\n");
226 return -1;
227 }
228
229 dprintf(INFO, "FOTA partition written successfully!");
230 return 0;
231}
232
233int get_boot_info_apps (char type, unsigned int *status)
234{
235 boot_info_for_apps apps_boot_info;
236 int ret = 0;
237
238 ret = smem_read_alloc_entry(SMEM_BOOT_INFO_FOR_APPS,
239 &apps_boot_info, sizeof(apps_boot_info));
240 if (ret)
241 {
242 dprintf(CRITICAL, "ERROR: unable to read shared memory for apps boot info %d\n",ret);
243 return ret;
244 }
245
246 dprintf(INFO,"boot flag %x update status %x\n",apps_boot_info.boot_flags,
247 apps_boot_info.status.update_status);
248
249 if(type == BOOT_FLAGS)
250 *status = apps_boot_info.boot_flags;
251 else if(type == UPDATE_STATUS)
252 *status = apps_boot_info.status.update_status;
253
254 return ret;
255}
256
Shashank Mittal024c0332010-02-03 11:44:00 -0800257/* Bootloader / Recovery Flow
258 *
259 * On every boot, the bootloader will read the recovery_message
260 * from flash and check the command field. The bootloader should
261 * deal with the command field not having a 0 terminator correctly
262 * (so as to not crash if the block is invalid or corrupt).
263 *
264 * The bootloader will have to publish the partition that contains
265 * the recovery_message to the linux kernel so it can update it.
266 *
267 * if command == "boot-recovery" -> boot recovery.img
268 * else if command == "update-radio" -> update radio image (below)
269 * else -> boot boot.img (normal boot)
270 *
271 * Radio Update Flow
272 * 1. the bootloader will attempt to load and validate the header
273 * 2. if the header is invalid, status="invalid-update", goto #8
274 * 3. display the busy image on-screen
275 * 4. if the update image is invalid, status="invalid-radio-image", goto #8
276 * 5. attempt to update the firmware (depending on the command)
277 * 6. if successful, status="okay", goto #8
278 * 7. if failed, and the old image can still boot, status="failed-update"
279 * 8. write the recovery_message, leaving the recovery field
280 * unchanged, updating status, and setting command to
281 * "boot-recovery"
282 * 9. reboot
283 *
284 * The bootloader will not modify or erase the cache partition.
285 * It is recovery's responsibility to clean up the mess afterwards.
286 */
287
288int recovery_init (void)
289{
290 struct recovery_message msg;
Shashank Mittal024c0332010-02-03 11:44:00 -0800291 char partition_name[32];
292 unsigned valid_command = 0;
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700293 int update_status = 0;
Shashank Mittal024c0332010-02-03 11:44:00 -0800294
295 // get recovery message
Greg Griscod6250552011-06-29 14:40:23 -0700296 if (get_recovery_message(&msg))
Shashank Mittal024c0332010-02-03 11:44:00 -0800297 return -1;
Shashank Mittal024c0332010-02-03 11:44:00 -0800298 msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination
Pavel Nedev96a9aea2013-02-26 15:16:26 -0800299 if (msg.command[0] != 0 && msg.command[0] != 255) {
300 dprintf(INFO,"Recovery command: %d %s\n",
301 sizeof(msg.command), msg.command);
302 }
Shashank Mittal024c0332010-02-03 11:44:00 -0800303
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700304 if (!strcmp("boot-recovery",msg.command))
305 {
306 if(!strcmp("RADIO",msg.status))
307 {
308 /* We're now here due to radio update, so check for update status */
Greg Griscod2471ef2011-07-14 13:00:42 -0700309 int ret = get_boot_info_apps(UPDATE_STATUS, (unsigned int *) &update_status);
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700310
311 if(!ret && (update_status & 0x01))
312 {
313 dprintf(INFO,"radio update success\n");
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700314 strlcpy(msg.status, "OKAY", sizeof(msg.status));
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700315 }
316 else
317 {
318 dprintf(INFO,"radio update failed\n");
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700319 strlcpy(msg.status, "failed-update", sizeof(msg.status));
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700320 }
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700321 strlcpy(msg.command, "", sizeof(msg.command)); // clearing recovery command
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700322 set_recovery_message(&msg); // send recovery message
323 boot_into_recovery = 1; // Boot in recovery mode
324 return 0;
325 }
326
Shashank Mittal024c0332010-02-03 11:44:00 -0800327 valid_command = 1;
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700328 strlcpy(msg.command, "", sizeof(msg.command)); // to safe against multiple reboot into recovery
329 strlcpy(msg.status, "OKAY", sizeof(msg.status));
Shashank Mittal024c0332010-02-03 11:44:00 -0800330 set_recovery_message(&msg); // send recovery message
331 boot_into_recovery = 1; // Boot in recovery mode
332 return 0;
333 }
334
335 if (!strcmp("update-radio",msg.command)) {
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700336 dprintf(INFO,"start radio update\n");
Shashank Mittal024c0332010-02-03 11:44:00 -0800337 valid_command = 1;
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700338 strlcpy(partition_name, "FOTA", sizeof(partition_name));
Shashank Mittal024c0332010-02-03 11:44:00 -0800339 }
340
341 //Todo: Add support for bootloader update too.
342
343 if(!valid_command) {
344 //We need not to do anything
345 return 0; // Boot in normal mode
346 }
347
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700348#ifdef OLD_FOTA_UPGRADE
Shashank Mittal024c0332010-02-03 11:44:00 -0800349 if (read_update_header_for_bootloader(&header)) {
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700350 strlcpy(msg.status, "invalid-update", sizeof(msg.status));
Shashank Mittal024c0332010-02-03 11:44:00 -0800351 goto SEND_RECOVERY_MSG;
352 }
353
354 if (update_firmware_image (&header, partition_name)) {
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700355 strlcpy(msg.status, "failed-update", sizeof(msg.status));
Shashank Mittal024c0332010-02-03 11:44:00 -0800356 goto SEND_RECOVERY_MSG;
357 }
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700358#else
359 if (set_ssd_radio_update(partition_name)) {
360 /* If writing to FOTA partition fails */
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700361 strlcpy(msg.command, "", sizeof(msg.command));
362 strlcpy(msg.status, "failed-update", sizeof(msg.status));
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700363 goto SEND_RECOVERY_MSG;
364 }
365 else {
366 /* Setting this to check the radio update status */
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700367 strlcpy(msg.command, "boot-recovery", sizeof(msg.command));
Ajay Dudanif4caa942011-10-02 08:57:15 -0700368 strlcpy(msg.status, "RADIO", sizeof(msg.status));
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700369 goto SEND_RECOVERY_MSG;
370 }
371#endif
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700372 strlcpy(msg.status, "OKAY", sizeof(msg.status));
Shashank Mittal024c0332010-02-03 11:44:00 -0800373
374SEND_RECOVERY_MSG:
Shashank Mittal024c0332010-02-03 11:44:00 -0800375 set_recovery_message(&msg); // send recovery message
376 boot_into_recovery = 1; // Boot in recovery mode
377 reboot_device(0);
378 return 0;
379}
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700380
381static int emmc_set_recovery_msg(struct recovery_message *out)
382{
383 char *ptn_name = "misc";
384 unsigned long long ptn = 0;
385 unsigned int size = ROUND_TO_PAGE(sizeof(*out),511);
386 unsigned char data[size];
Kinson Chikf1a43512011-07-14 11:28:39 -0700387 int index = INVALID_PTN;
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700388
Kinson Chikf1a43512011-07-14 11:28:39 -0700389 index = partition_get_index((unsigned char *) ptn_name);
390 ptn = partition_get_offset(index);
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700391 if(ptn == 0) {
392 dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name);
393 return -1;
394 }
395 memcpy(data, out, sizeof(*out));
396 if (mmc_write(ptn , size, (unsigned int*)data)) {
397 dprintf(CRITICAL,"mmc write failure %s %d\n",ptn_name, sizeof(*out));
398 return -1;
399 }
400 return 0;
401}
402
403static int emmc_get_recovery_msg(struct recovery_message *in)
404{
405 char *ptn_name = "misc";
406 unsigned long long ptn = 0;
407 unsigned int size = ROUND_TO_PAGE(sizeof(*in),511);
408 unsigned char data[size];
Kinson Chikf1a43512011-07-14 11:28:39 -0700409 int index = INVALID_PTN;
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700410
Kinson Chikf1a43512011-07-14 11:28:39 -0700411 index = partition_get_index((unsigned char *) ptn_name);
412 ptn = partition_get_offset(index);
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700413 if(ptn == 0) {
414 dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name);
415 return -1;
416 }
417 if (mmc_read(ptn , (unsigned int*)data, size)) {
418 dprintf(CRITICAL,"mmc read failure %s %d\n",ptn_name, size);
419 return -1;
420 }
421 memcpy(in, data, sizeof(*in));
422 return 0;
423}
424
425int _emmc_recovery_init(void)
426{
427 int update_status = 0;
428 struct recovery_message msg;
429
430 // get recovery message
431 if(emmc_get_recovery_msg(&msg))
432 return -1;
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700433 msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination
Pavel Nedev96a9aea2013-02-26 15:16:26 -0800434 if (msg.command[0] != 0 && msg.command[0] != 255) {
435 dprintf(INFO,"Recovery command: %d %s\n",
436 sizeof(msg.command), msg.command);
437 }
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700438
439 if (!strcmp("update-radio",msg.command))
440 {
441 /* We're now here due to radio update, so check for update status */
Greg Griscod2471ef2011-07-14 13:00:42 -0700442 int ret = get_boot_info_apps(UPDATE_STATUS, (unsigned int *) &update_status);
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700443
444 if(!ret && (update_status & 0x01))
445 {
446 dprintf(INFO,"radio update success\n");
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700447 strlcpy(msg.status, "OKAY", sizeof(msg.status));
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700448 }
449 else
450 {
451 dprintf(INFO,"radio update failed\n");
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700452 strlcpy(msg.status, "failed-update", sizeof(msg.status));
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700453 }
Shashank Mittal162244e2011-08-08 19:01:25 -0700454 boot_into_recovery = 1; // Boot in recovery mode
455 }
456 if (!strcmp("reset-device-info",msg.command))
457 {
458 reset_device_info();
459 }
460 if (!strcmp("root-detect",msg.command))
461 {
462 set_device_root();
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700463 }
464 else
465 return 0; // do nothing
466
Ajay Dudanif63d02f2011-10-01 08:29:53 -0700467 strlcpy(msg.command, "", sizeof(msg.command)); // clearing recovery command
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700468 emmc_set_recovery_msg(&msg); // send recovery message
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700469 return 0;
470}