blob: 4584cf2e2dc3d331f83842eb8191d19507a12af3 [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"
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -070042#include "smem.h"
43
44#define BOOT_FLAGS 1
45#define UPDATE_STATUS 2
46#define ROUND_TO_PAGE(x,y) (((x) + (y)) & (~(y)))
Shashank Mittal024c0332010-02-03 11:44:00 -080047
48static const int MISC_PAGES = 3; // number of pages to save
49static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
50static char buf[4096];
51unsigned boot_into_recovery = 0;
52
53void reboot_device(unsigned);
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -070054extern int target_is_emmc_boot(void);
Shashank Mittal024c0332010-02-03 11:44:00 -080055
56int get_recovery_message(struct recovery_message *out)
57{
58 struct ptentry *ptn;
59 struct ptable *ptable;
60 unsigned offset = 0;
61 unsigned pagesize = flash_page_size();
62
63 ptable = flash_get_ptable();
64
65 if (ptable == NULL) {
66 dprintf(CRITICAL, "ERROR: Partition table not found\n");
67 return -1;
68 }
69 ptn = ptable_find(ptable, "misc");
70
71 if (ptn == NULL) {
72 dprintf(CRITICAL, "ERROR: No misc partition found\n");
73 return -1;
74 }
75
76 offset += (pagesize * MISC_COMMAND_PAGE);
77 if (flash_read(ptn, offset, buf, pagesize)) {
78 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
79 return -1;
80 }
81 memcpy(out, buf, sizeof(*out));
82 return 0;
83}
84
85int set_recovery_message(const struct recovery_message *in)
86{
87 struct ptentry *ptn;
88 struct ptable *ptable;
89 unsigned offset = 0;
90 unsigned pagesize = flash_page_size();
91 unsigned n = 0;
92
93 ptable = flash_get_ptable();
94
95 if (ptable == NULL) {
96 dprintf(CRITICAL, "ERROR: Partition table not found\n");
97 return -1;
98 }
99 ptn = ptable_find(ptable, "misc");
100
101 if (ptn == NULL) {
102 dprintf(CRITICAL, "ERROR: No misc partition found\n");
103 return -1;
104 }
105
106 n = pagesize * (MISC_COMMAND_PAGE + 1);
107
108 if (flash_read(ptn, offset, SCRATCH_ADDR, n)) {
109 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
110 return -1;
111 }
112
113 offset += (pagesize * MISC_COMMAND_PAGE);
114 offset += SCRATCH_ADDR;
115 memcpy(offset, in, sizeof(*in));
116 if (flash_write(ptn, 0, (void *)SCRATCH_ADDR, n)) {
117 dprintf(CRITICAL, "ERROR: flash write fail!\n");
118 return -1;
119 }
120}
121
122int read_update_header_for_bootloader(struct update_header *header)
123{
124 struct ptentry *ptn;
125 struct ptable *ptable;
126 unsigned offset = 0;
127 unsigned pagesize = flash_page_size();
128
129 ptable = flash_get_ptable();
130 if (ptable == NULL) {
131 dprintf(CRITICAL, "ERROR: Partition table not found\n");
132 return -1;
133 }
134 ptn = ptable_find(ptable, "cache");
135
136 if (ptn == NULL) {
137 dprintf(CRITICAL, "ERROR: No cache partition found\n");
138 return -1;
139 }
140 if (flash_read(ptn, offset, buf, pagesize)) {
141 dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n");
142 return -1;
143 }
144 memcpy(header, buf, sizeof(*header));
145
146 if(strncmp(header->MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE))
147 {
148 return -1;
149 }
150 return 0;
151}
152
153int update_firmware_image (struct update_header *header, char *name)
154{
155 struct ptentry *ptn;
156 struct ptable *ptable;
157 unsigned offset = 0;
158 unsigned pagesize = flash_page_size();
159 unsigned pagemask = pagesize -1;
160 unsigned n = 0;
161
162 ptable = flash_get_ptable();
163 if (ptable == NULL) {
164 dprintf(CRITICAL, "ERROR: Partition table not found\n");
165 return -1;
166 }
167
168 ptn = ptable_find(ptable, "cache");
169 if (ptn == NULL) {
170 dprintf(CRITICAL, "ERROR: No cache partition found\n");
171 return -1;
172 }
173
174 offset += header->image_offset;
175 n = (header->image_length + pagemask) & (~pagemask);
176
177 if (flash_read(ptn, offset, SCRATCH_ADDR, n)) {
178 dprintf(CRITICAL, "ERROR: Cannot read radio image\n");
179 return -1;
180 }
181
182 ptn = ptable_find(ptable, name);
183 if (ptn == NULL) {
184 dprintf(CRITICAL, "ERROR: No %s partition found\n", name);
185 return -1;
186 }
187
188 if (flash_write(ptn, 0, SCRATCH_ADDR, n)) {
189 dprintf(CRITICAL, "ERROR: flash write fail!\n");
190 return -1;
191 }
192
193 dprintf(INFO, "Partition writen successfully!");
194 return 0;
195}
196
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700197static int set_ssd_radio_update (char *name)
198{
199 struct ptentry *ptn;
200 struct ptable *ptable;
201 unsigned int ssd_cookie[2] = {0x53534443, 0x4F4F4B49};
202 unsigned pagesize = flash_page_size();
203 unsigned pagemask = pagesize -1;
204 unsigned n = 0;
205
206 ptable = flash_get_ptable();
207 if (ptable == NULL) {
208 dprintf(CRITICAL, "ERROR: Partition table not found\n");
209 return -1;
210 }
211
212 n = (sizeof(ssd_cookie) + pagemask) & (~pagemask);
213
214 ptn = ptable_find(ptable, name);
215 if (ptn == NULL) {
216 dprintf(CRITICAL, "ERROR: No %s partition found\n", name);
217 return -1;
218 }
219
220 if (flash_write(ptn, 0, ssd_cookie, n)) {
221 dprintf(CRITICAL, "ERROR: flash write fail!\n");
222 return -1;
223 }
224
225 dprintf(INFO, "FOTA partition written successfully!");
226 return 0;
227}
228
229int get_boot_info_apps (char type, unsigned int *status)
230{
231 boot_info_for_apps apps_boot_info;
232 int ret = 0;
233
234 ret = smem_read_alloc_entry(SMEM_BOOT_INFO_FOR_APPS,
235 &apps_boot_info, sizeof(apps_boot_info));
236 if (ret)
237 {
238 dprintf(CRITICAL, "ERROR: unable to read shared memory for apps boot info %d\n",ret);
239 return ret;
240 }
241
242 dprintf(INFO,"boot flag %x update status %x\n",apps_boot_info.boot_flags,
243 apps_boot_info.status.update_status);
244
245 if(type == BOOT_FLAGS)
246 *status = apps_boot_info.boot_flags;
247 else if(type == UPDATE_STATUS)
248 *status = apps_boot_info.status.update_status;
249
250 return ret;
251}
252
Shashank Mittal024c0332010-02-03 11:44:00 -0800253/* Bootloader / Recovery Flow
254 *
255 * On every boot, the bootloader will read the recovery_message
256 * from flash and check the command field. The bootloader should
257 * deal with the command field not having a 0 terminator correctly
258 * (so as to not crash if the block is invalid or corrupt).
259 *
260 * The bootloader will have to publish the partition that contains
261 * the recovery_message to the linux kernel so it can update it.
262 *
263 * if command == "boot-recovery" -> boot recovery.img
264 * else if command == "update-radio" -> update radio image (below)
265 * else -> boot boot.img (normal boot)
266 *
267 * Radio Update Flow
268 * 1. the bootloader will attempt to load and validate the header
269 * 2. if the header is invalid, status="invalid-update", goto #8
270 * 3. display the busy image on-screen
271 * 4. if the update image is invalid, status="invalid-radio-image", goto #8
272 * 5. attempt to update the firmware (depending on the command)
273 * 6. if successful, status="okay", goto #8
274 * 7. if failed, and the old image can still boot, status="failed-update"
275 * 8. write the recovery_message, leaving the recovery field
276 * unchanged, updating status, and setting command to
277 * "boot-recovery"
278 * 9. reboot
279 *
280 * The bootloader will not modify or erase the cache partition.
281 * It is recovery's responsibility to clean up the mess afterwards.
282 */
283
284int recovery_init (void)
285{
286 struct recovery_message msg;
287 struct update_header header;
288 char partition_name[32];
289 unsigned valid_command = 0;
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700290 int update_status = 0;
Shashank Mittal024c0332010-02-03 11:44:00 -0800291
292 // get recovery message
293 if(get_recovery_message(&msg))
294 return -1;
295 if (msg.command[0] != 0 && msg.command[0] != 255) {
296 dprintf("Recovery command: %.*s\n", sizeof(msg.command), msg.command);
297 }
298 msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination
299
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700300 if (!strcmp("boot-recovery",msg.command))
301 {
302 if(!strcmp("RADIO",msg.status))
303 {
304 /* We're now here due to radio update, so check for update status */
305 int ret = get_boot_info_apps(UPDATE_STATUS, &update_status);
306
307 if(!ret && (update_status & 0x01))
308 {
309 dprintf(INFO,"radio update success\n");
310 strcpy(msg.status, "OKAY");
311 }
312 else
313 {
314 dprintf(INFO,"radio update failed\n");
315 strcpy(msg.status, "failed-update");
316 }
317 strcpy(msg.command, ""); // clearing recovery command
318 set_recovery_message(&msg); // send recovery message
319 boot_into_recovery = 1; // Boot in recovery mode
320 return 0;
321 }
322
Shashank Mittal024c0332010-02-03 11:44:00 -0800323 valid_command = 1;
324 strcpy(msg.command, ""); // to safe against multiple reboot into recovery
325 strcpy(msg.status, "OKAY");
326 set_recovery_message(&msg); // send recovery message
327 boot_into_recovery = 1; // Boot in recovery mode
328 return 0;
329 }
330
331 if (!strcmp("update-radio",msg.command)) {
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700332 dprintf(INFO,"start radio update\n");
Shashank Mittal024c0332010-02-03 11:44:00 -0800333 valid_command = 1;
Subbaraman Narayanamurthyeb92bcc2010-07-20 14:32:46 -0700334 strcpy(partition_name, "FOTA");
Shashank Mittal024c0332010-02-03 11:44:00 -0800335 }
336
337 //Todo: Add support for bootloader update too.
338
339 if(!valid_command) {
340 //We need not to do anything
341 return 0; // Boot in normal mode
342 }
343
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700344#ifdef OLD_FOTA_UPGRADE
Shashank Mittal024c0332010-02-03 11:44:00 -0800345 if (read_update_header_for_bootloader(&header)) {
346 strcpy(msg.status, "invalid-update");
347 goto SEND_RECOVERY_MSG;
348 }
349
350 if (update_firmware_image (&header, partition_name)) {
351 strcpy(msg.status, "failed-update");
352 goto SEND_RECOVERY_MSG;
353 }
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700354#else
355 if (set_ssd_radio_update(partition_name)) {
356 /* If writing to FOTA partition fails */
357 strcpy(msg.command, "");
358 strcpy(msg.status, "failed-update");
359 goto SEND_RECOVERY_MSG;
360 }
361 else {
362 /* Setting this to check the radio update status */
363 strcpy(msg.command, "boot-recovery");
364 strcpy(msg.status, "RADIO");
365 goto SEND_RECOVERY_MSG;
366 }
367#endif
Shashank Mittal024c0332010-02-03 11:44:00 -0800368 strcpy(msg.status, "OKAY");
369
370SEND_RECOVERY_MSG:
Shashank Mittal024c0332010-02-03 11:44:00 -0800371 set_recovery_message(&msg); // send recovery message
372 boot_into_recovery = 1; // Boot in recovery mode
373 reboot_device(0);
374 return 0;
375}
Subbaraman Narayanamurthy0e445b02011-06-19 21:34:46 -0700376
377static int emmc_set_recovery_msg(struct recovery_message *out)
378{
379 char *ptn_name = "misc";
380 unsigned long long ptn = 0;
381 unsigned int size = ROUND_TO_PAGE(sizeof(*out),511);
382 unsigned char data[size];
383
384 ptn = mmc_ptn_offset(ptn_name);
385 if(ptn == 0) {
386 dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name);
387 return -1;
388 }
389 memcpy(data, out, sizeof(*out));
390 if (mmc_write(ptn , size, (unsigned int*)data)) {
391 dprintf(CRITICAL,"mmc write failure %s %d\n",ptn_name, sizeof(*out));
392 return -1;
393 }
394 return 0;
395}
396
397static int emmc_get_recovery_msg(struct recovery_message *in)
398{
399 char *ptn_name = "misc";
400 unsigned long long ptn = 0;
401 unsigned int size = ROUND_TO_PAGE(sizeof(*in),511);
402 unsigned char data[size];
403
404 ptn = mmc_ptn_offset(ptn_name);
405 if(ptn == 0) {
406 dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name);
407 return -1;
408 }
409 if (mmc_read(ptn , (unsigned int*)data, size)) {
410 dprintf(CRITICAL,"mmc read failure %s %d\n",ptn_name, size);
411 return -1;
412 }
413 memcpy(in, data, sizeof(*in));
414 return 0;
415}
416
417int _emmc_recovery_init(void)
418{
419 int update_status = 0;
420 struct recovery_message msg;
421
422 // get recovery message
423 if(emmc_get_recovery_msg(&msg))
424 return -1;
425 if (msg.command[0] != 0 && msg.command[0] != 255) {
426 dprintf(INFO,"Recovery command: %d %s\n", sizeof(msg.command), msg.command);
427 }
428 msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination
429
430 if (!strcmp("update-radio",msg.command))
431 {
432 /* We're now here due to radio update, so check for update status */
433 int ret = get_boot_info_apps(UPDATE_STATUS, &update_status);
434
435 if(!ret && (update_status & 0x01))
436 {
437 dprintf(INFO,"radio update success\n");
438 strcpy(msg.status, "OKAY");
439 }
440 else
441 {
442 dprintf(INFO,"radio update failed\n");
443 strcpy(msg.status, "failed-update");
444 }
445 }
446 else
447 return 0; // do nothing
448
449 strcpy(msg.command, ""); // clearing recovery command
450 emmc_set_recovery_msg(&msg); // send recovery message
451 boot_into_recovery = 1; // Boot in recovery mode
452 return 0;
453}