summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-07-10 19:05:29 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-11 15:39:40 +0100
commite9d228832ccbee7e486c45a51680c6f94ce1ab92 (patch)
tree494348b9ccd742fc5df3568cf0740062974172ef
parentd6dcb996847b547e1e980884fbc06966293a7f6a (diff)
downloadrockbox-e9d228832ccbee7e486c45a51680c6f94ce1ab92.tar.gz
rockbox-e9d228832ccbee7e486c45a51680c6f94ce1ab92.zip
x1000: Unified bootloader
Change-Id: Ib1f2ca2a376866c61dd1bd62abd6e31210d11e5c
-rw-r--r--bootloader/SOURCES2
-rw-r--r--bootloader/fiiom3k.c360
-rw-r--r--bootloader/x1000.c490
3 files changed, 491 insertions, 361 deletions
diff --git a/bootloader/SOURCES b/bootloader/SOURCES
index 57c23b115c..f72c58a0b7 100644
--- a/bootloader/SOURCES
+++ b/bootloader/SOURCES
@@ -90,5 +90,5 @@ show_logo.c
90sansaconnect.c 90sansaconnect.c
91show_logo.c 91show_logo.c
92#elif defined(FIIO_M3K) 92#elif defined(FIIO_M3K)
93fiiom3k.c 93x1000.c
94#endif 94#endif
diff --git a/bootloader/fiiom3k.c b/bootloader/fiiom3k.c
deleted file mode 100644
index 42b3260e07..0000000000
--- a/bootloader/fiiom3k.c
+++ /dev/null
@@ -1,360 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "system.h"
23#include "core_alloc.h"
24#include "kernel/kernel-internal.h"
25#include "i2c.h"
26#include "power.h"
27#include "lcd.h"
28#include "font.h"
29#include "backlight.h"
30#include "backlight-target.h"
31#include "button.h"
32#include "storage.h"
33#include "file_internal.h"
34#include "disk.h"
35#include "usb.h"
36#include "rb-loader.h"
37#include "loader_strerror.h"
38#include "version.h"
39#include "installer-fiiom3k.h"
40#include "boot-x1000.h"
41#include "x1000/cpm.h"
42
43/* Load address where the binary needs to be placed */
44extern unsigned char loadaddress[];
45
46/* Fixed buffer to contain the loaded binary in memory */
47extern unsigned char loadbuffer[];
48extern unsigned char loadbufferend[];
49#define MAX_LOAD_SIZE (loadbufferend - loadbuffer)
50
51void exec(void* dst, const void* src, int bytes)
52 __attribute__((noreturn, section(".icode")));
53
54void exec(void* dst, const void* src, int bytes)
55{
56 memcpy(dst, src, bytes);
57 commit_discard_idcache();
58 __asm__ __volatile__ ("jr %0\n"
59 "nop\n"
60 :: "r"(dst));
61 __builtin_unreachable();
62}
63
64static bool lcd_inited = false;
65static bool usb_inited = false;
66static bool disk_inited = false;
67
68static void init_lcd(void)
69{
70 if(lcd_inited)
71 return;
72
73 lcd_init();
74 font_init();
75 lcd_setfont(FONT_SYSFIXED);
76
77 /* Clear screen before turning backlight on, otherwise we might
78 * display random garbage on the screen */
79 lcd_clear_display();
80 lcd_update();
81
82 backlight_init();
83
84 lcd_inited = true;
85}
86
87static void put_version(void)
88{
89 lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2,
90 (LCD_HEIGHT - SYSFONT_HEIGHT), rbversion);
91}
92
93static void do_splash2(int delay, const char* msg, const char* msg2)
94{
95 init_lcd();
96 lcd_clear_display();
97 lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg))) / 2,
98 (LCD_HEIGHT - SYSFONT_HEIGHT) / 2, msg);
99 if(msg2) {
100 lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg2))) / 2,
101 (LCD_HEIGHT + 2*SYSFONT_HEIGHT) / 2, msg2);
102 }
103
104 put_version();
105 lcd_update();
106 sleep(delay);
107}
108
109static void do_splash(int delay, const char* msg)
110{
111 do_splash2(delay, msg, NULL);
112}
113
114static void do_usb(void)
115{
116 if(!usb_inited) {
117 usb_init();
118 usb_start_monitoring();
119 usb_inited = true;
120 }
121
122 do_splash2(0, "Waiting for USB", "Press POWER to go back");
123
124 int btn;
125 while(1) {
126 btn = button_get(true);
127 if(btn == SYS_USB_CONNECTED)
128 break;
129 else if(btn == BUTTON_POWER)
130 return;
131 }
132
133 do_splash(0, "USB mode");
134 usb_acknowledge(SYS_USB_CONNECTED_ACK);
135 while(button_get(true) != SYS_USB_DISCONNECTED);
136
137 do_splash(3*HZ, "USB disconnected");
138}
139
140static int init_disk(void)
141{
142 if(disk_inited)
143 return 0;
144
145 while(!storage_present(0)) {
146 do_splash2(0, "Insert SD card", "Press POWER for recovery");
147 int btn = button_get_w_tmo(HZ);
148 if(btn == BUTTON_POWER)
149 return 1;
150 }
151
152 if(disk_mount_all() <= 0) {
153 do_splash(5*HZ, "Cannot mount filesystem");
154 return 1;
155 }
156
157 disk_inited = true;
158 return 0;
159}
160
161static void do_boot(void)
162{
163 if(init_disk() != 0)
164 return;
165
166 int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
167 if(loadsize <= 0) {
168 do_splash2(5*HZ, "Error loading Rockbox",
169 loader_strerror(loadsize));
170 do_usb();
171 return;
172 }
173
174 if(lcd_inited)
175 backlight_hw_off();
176
177 disable_irq();
178 exec(loadaddress, loadbuffer, loadsize);
179}
180
181#define INSTALL 0
182#define BACKUP 1
183#define RESTORE 2
184
185static void do_install(int which)
186{
187 int rc = init_disk();
188 if(rc != 0) {
189 do_splash2(5*HZ, "Install aborted", "No SD card present");
190 return;
191 }
192
193 const char* msg;
194 if(rc == INSTALL)
195 msg = "Installing";
196 else if(rc == BACKUP)
197 msg = "Backing up";
198 else
199 msg = "Restoring backup";
200
201 do_splash(0, msg);
202
203 if(which == INSTALL)
204 rc = install_boot("/bootloader.m3k");
205 else if(which == BACKUP)
206 rc = backup_boot("/fiiom3k-boot.bin");
207 else
208 rc = restore_boot("/fiiom3k-boot.bin");
209
210 char buf[32];
211 snprintf(buf, sizeof(buf), "Failed! Error: %d", rc);
212 const char* msg1 = rc == 0 ? "Success" : buf;
213 const char* msg2 = "Press POWER to continue";
214 do_splash2(0, msg1, msg2);
215
216 button_clear_queue();
217 while(button_get(true) != BUTTON_POWER);
218}
219
220static void recovery_menu(void)
221{
222 static const char* items[] = {
223 "--- Rockbox recovery menu ---",
224 "[System]",
225 " Start Rockbox",
226 " USB mode",
227 " Shutdown",
228 " Reboot",
229 "[Bootloader]",
230 " Install or update",
231 " Backup",
232 " Restore",
233 "",
234 "",
235 "",
236 "",
237 "",
238 "VOL+/VOL- move cursor",
239 "PLAY select item",
240 "POWER power off",
241 };
242
243 static const int nitems = sizeof(items) / sizeof(char*);
244
245 init_lcd();
246
247 int selection = 2;
248 do {
249 /* Draw menu */
250 lcd_clear_display();
251
252 for(int i = 0; i < nitems; ++i)
253 lcd_puts(0, i, items[i]);
254
255 if(items[selection][0] == ' ')
256 lcd_puts(0, selection, "=>");
257
258 put_version();
259 lcd_update();
260
261 /* Clear queue to avoid accidental input */
262 button_clear_queue();
263
264 /* Get the button */
265 int btn = button_get(true);
266
267 /* Process user input */
268 if(btn == BUTTON_VOL_UP) {
269 for(int i = selection-1; i >= 0; --i) {
270 if(items[i][0] == ' ') {
271 selection = i;
272 break;
273 }
274 }
275
276 continue;
277 } else if(btn == BUTTON_VOL_DOWN) {
278 for(int i = selection+1; i < nitems; ++i) {
279 if(items[i][0] == ' ') {
280 selection = i;
281 break;
282 }
283 }
284
285 continue;
286 } else if(btn == BUTTON_POWER) {
287 selection = 4; /* Shutdown */
288 } else if(btn != BUTTON_PLAY) {
289 continue;
290 }
291
292 /* User pressed PLAY so decide what action to take */
293 switch(selection) {
294 case 2: /* Start rockbox */
295 do_boot();
296 break;
297
298 case 3: /* USB mode */
299 do_usb();
300 break;
301
302 case 4: /* Shutdown */
303 do_splash(HZ, "Shutting down");
304 power_off();
305 break;
306
307 case 5: /* Reboot */
308 do_splash(HZ, "Rebooting");
309 system_reboot();
310 break;
311
312 case 7: /* Install bootloader */
313 do_install(INSTALL);
314 break;
315
316 case 8: /* Backup bootloader */
317 do_install(BACKUP);
318 break;
319
320 case 9: /* Restore bootloader */
321 do_install(RESTORE);
322 break;
323
324 default:
325 break;
326 }
327 } while(1);
328}
329
330void main(void)
331{
332 bool recovery_mode = false;
333
334 if(get_boot_flag(BOOT_FLAG_USB_BOOT))
335 recovery_mode = true;
336
337 system_init();
338 core_allocator_init();
339 kernel_init();
340 i2c_init();
341 power_init();
342 button_init();
343 enable_irq();
344
345 if(storage_init() < 0) {
346 do_splash(3*HZ, "Failed to init storage");
347 power_off();
348 }
349
350 filesystem_init();
351
352 if(button_read_device() & BUTTON_VOL_UP)
353 recovery_mode = true;
354
355 if(!recovery_mode)
356 do_boot();
357
358 /* If boot fails or user holds Vol+, go to recovery menu */
359 recovery_menu();
360}
diff --git a/bootloader/x1000.c b/bootloader/x1000.c
new file mode 100644
index 0000000000..0b28b7449d
--- /dev/null
+++ b/bootloader/x1000.c
@@ -0,0 +1,490 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* Unified bootloader for all X1000 targets. This is a bit messy.
23 *
24 * Features:
25 * - Text based user interface
26 * - USB mass storage access
27 * - Bootloader installation / backup / restore
28 *
29 * Possible future improvements:
30 * - Allow booting original firmware from the UI
31 */
32
33#include "system.h"
34#include "core_alloc.h"
35#include "kernel/kernel-internal.h"
36#include "i2c.h"
37#include "power.h"
38#include "lcd.h"
39#include "font.h"
40#include "backlight.h"
41#include "backlight-target.h"
42#include "button.h"
43#include "storage.h"
44#include "file_internal.h"
45#include "disk.h"
46#include "usb.h"
47#include "rb-loader.h"
48#include "loader_strerror.h"
49#include "version.h"
50#include "boot-x1000.h"
51#include <stddef.h>
52#include <stdbool.h>
53#include <string.h>
54#include <stdio.h>
55#include <stdarg.h>
56
57#ifdef FIIO_M3K
58# include "installer-fiiom3k.h"
59#endif
60
61#if defined(FIIO_M3K)
62# define BL_RECOVERY BUTTON_VOL_UP
63# define BL_UP BUTTON_VOL_UP
64# define BL_DOWN BUTTON_VOL_DOWN
65# define BL_SELECT BUTTON_PLAY
66# define BL_QUIT BUTTON_POWER
67# define BL_UP_NAME "VOL+"
68# define BL_DOWN_NAME "VOL-"
69# define BL_SELECT_NAME "PLAY"
70# define BL_QUIT_NAME "POWER"
71#else
72# error "Missing keymap!"
73#endif
74
75enum {
76 MENUITEM_HEADING,
77 MENUITEM_ACTION,
78};
79
80struct menuitem {
81 int type;
82 const char* text;
83 void(*action)(void);
84};
85
86void clearscreen(void);
87void putversion(void);
88void putcenter_y(int y, const char* msg);
89void putcenter_line(int line, const char* msg);
90void splash2(long delay, const char* msg, const char* msg2);
91void splash(long delay, const char* msg);
92
93void init_lcd(void);
94void init_usb(void);
95int init_disk(void);
96
97void recovery_menu(void) __attribute__((noreturn));
98
99void boot_rockbox(void);
100void usb_mode(void);
101void shutdown(void);
102void reboot(void);
103void bootloader_install(void);
104void bootloader_backup(void);
105void bootloader_restore(void);
106
107/* Defines the recovery menu contents */
108const struct menuitem recovery_items[] = {
109 {MENUITEM_HEADING, "System", NULL},
110 {MENUITEM_ACTION, "Start Rockbox", &boot_rockbox},
111 {MENUITEM_ACTION, "USB mode", &usb_mode},
112 {MENUITEM_ACTION, "Shutdown", &shutdown},
113 {MENUITEM_ACTION, "Reboot", &reboot},
114 {MENUITEM_HEADING, "Bootloader", NULL},
115 {MENUITEM_ACTION, "Install or update", &bootloader_install},
116 {MENUITEM_ACTION, "Backup", &bootloader_backup},
117 {MENUITEM_ACTION, "Restore", &bootloader_restore},
118};
119
120/* Final load address of rockbox binary.
121 * NOTE: this is really the load address of the bootloader... it relies
122 * on the fact that bootloader and app are linked at the same address. */
123extern unsigned char loadaddress[];
124
125/* Temp buffer to contain the binary in memory */
126extern unsigned char loadbuffer[];
127extern unsigned char loadbufferend[];
128#define MAX_LOAD_SIZE (loadbufferend - loadbuffer)
129
130/* Flags to indicate if hardware was already initialized */
131bool lcd_inited = false;
132bool usb_inited = false;
133bool disk_inited = false;
134
135/* Jump to loaded binary */
136void exec(void* dst, const void* src, size_t bytes)
137 __attribute__((noinline, noreturn, section(".icode")));
138
139void exec(void* dst, const void* src, size_t bytes)
140{
141 memcpy(dst, src, bytes);
142 commit_discard_idcache();
143
144 typedef void(*entry_fn)(void) __attribute__((noreturn));
145 entry_fn fn = (entry_fn)dst;
146 fn();
147}
148
149void clearscreen(void)
150{
151 init_lcd();
152 lcd_clear_display();
153 putversion();
154}
155
156void putversion(void)
157{
158 int x = (LCD_WIDTH - SYSFONT_WIDTH*strlen(rbversion)) / 2;
159 int y = LCD_HEIGHT - SYSFONT_HEIGHT;
160 lcd_putsxy(x, y, rbversion);
161}
162
163void putcenter_y(int y, const char* msg)
164{
165 int x = (LCD_WIDTH - SYSFONT_WIDTH*strlen(msg)) / 2;
166 lcd_putsxy(x, y, msg);
167}
168
169void putcenter_line(int line, const char* msg)
170{
171 int y = LCD_HEIGHT/2 + (line - 1)*SYSFONT_HEIGHT;
172 putcenter_y(y, msg);
173}
174
175void splash2(long delay, const char* msg, const char* msg2)
176{
177 clearscreen();
178 putcenter_line(0, msg);
179 if(msg2)
180 putcenter_line(1, msg2);
181 lcd_update();
182 sleep(delay);
183}
184
185void splash(long delay, const char* msg)
186{
187 splash2(delay, msg, NULL);
188}
189
190void init_lcd(void)
191{
192 if(lcd_inited)
193 return;
194
195 lcd_init();
196 font_init();
197 lcd_setfont(FONT_SYSFIXED);
198
199 /* Clear screen before turning backlight on, otherwise we might
200 * display random garbage on the screen */
201 lcd_clear_display();
202 lcd_update();
203
204 backlight_init();
205
206 lcd_inited = true;
207}
208
209/* TODO: This does not work properly after a USB boot.
210 *
211 * The USB core is not properly reset by just re-initializing it, and I can't
212 * figure out how to make it work. Setting the DWC_DCTL.SDIS bit will force a
213 * disconnect (the usb-designware driver does this already as part of its init
214 * but it doesn't seem to cause a disconnect).
215 *
216 * But the host still doesn't detect us properly when we reconnect. Linux gives
217 * messages "usb 1-3: config 1 has no interfaces?" in dmesg and no mass storage
218 * interfaces show up.
219 *
220 * Re-plugging the cable seems to reset everything to a working state and there
221 * are no issues, but that's annoying.
222 */
223void init_usb(void)
224{
225 if(usb_inited)
226 return;
227
228 usb_init();
229 usb_start_monitoring();
230 usb_inited = true;
231}
232
233int init_disk(void)
234{
235 if(disk_inited)
236 return 0;
237
238 button_clear_queue();
239 while(!storage_present(IF_MD(0))) {
240 splash2(0, "Insert SD card", "Press " BL_QUIT_NAME " for recovery");
241 if(button_get_w_tmo(HZ/4) == BL_QUIT)
242 return -1;
243 }
244
245 if(disk_mount_all() <= 0) {
246 splash(5*HZ, "Cannot mount disk");
247 return -1;
248 }
249
250 disk_inited = true;
251 return 0;
252}
253
254void put_help_line(int line, const char* str1, const char* str2)
255{
256 int width = LCD_WIDTH / SYSFONT_WIDTH;
257 lcd_puts(0, line, str1);
258 lcd_puts(width - strlen(str2), line, str2);
259}
260
261void recovery_menu(void)
262{
263 const int n_items = sizeof(recovery_items)/sizeof(struct menuitem);
264
265 int selection = 0;
266 while(recovery_items[selection].type != MENUITEM_ACTION)
267 ++selection;
268
269 while(1) {
270 clearscreen();
271 putcenter_y(0, "Rockbox recovery menu");
272
273 int top_line = 2;
274
275 /* draw the menu */
276 for(int i = 0; i < n_items; ++i) {
277 switch(recovery_items[i].type) {
278 case MENUITEM_HEADING:
279 lcd_putsf(0, top_line+i, "[%s]", recovery_items[i].text);
280 break;
281
282 case MENUITEM_ACTION:
283 lcd_puts(3, top_line+i, recovery_items[i].text);
284 break;
285
286 default:
287 break;
288 }
289 }
290
291 /* draw the selection marker */
292 lcd_puts(0, top_line+selection, "=>");
293
294 /* draw the help text */
295 int line = (LCD_HEIGHT - SYSFONT_HEIGHT)/SYSFONT_HEIGHT - 3;
296 put_help_line(line++, BL_DOWN_NAME "/" BL_UP_NAME, "move cursor");
297 put_help_line(line++, BL_SELECT_NAME, "select item");
298 put_help_line(line++, BL_QUIT_NAME, "power off");
299
300 lcd_update();
301
302 /* handle input */
303 button_clear_queue();
304 switch(button_get(true)) {
305 case BL_SELECT: {
306 if(recovery_items[selection].action)
307 recovery_items[selection].action();
308 } break;
309
310 case BL_UP:
311 for(int i = selection-1; i >= 0; --i) {
312 if(recovery_items[i].action) {
313 selection = i;
314 break;
315 }
316 }
317 break;
318
319 case BL_DOWN:
320 for(int i = selection+1; i < n_items; ++i) {
321 if(recovery_items[i].action) {
322 selection = i;
323 break;
324 }
325 }
326 break;
327
328 case BL_QUIT:
329 shutdown();
330 break;
331
332 default:
333 break;
334 }
335 }
336}
337
338void boot_rockbox(void)
339{
340 if(init_disk() != 0)
341 return;
342
343 int rc = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
344 if(rc <= 0) {
345 splash2(5*HZ, "Error loading Rockbox", loader_strerror(rc));
346 return;
347 }
348
349 if(lcd_inited)
350 backlight_hw_off();
351
352 disable_irq();
353 exec(loadaddress, loadbuffer, rc);
354}
355
356void usb_mode(void)
357{
358 init_usb();
359 splash2(0, "Waiting for USB", "Press " BL_QUIT_NAME " to go back");
360
361 while(1) {
362 int btn = button_get(true);
363 if(btn == SYS_USB_CONNECTED)
364 break;
365 else if(btn == BL_QUIT)
366 return;
367 }
368
369 splash(0, "USB mode");
370 usb_acknowledge(SYS_USB_CONNECTED_ACK);
371 while(button_get(true) != SYS_USB_DISCONNECTED);
372
373 splash(3*HZ, "USB disconnected");
374}
375
376void shutdown(void)
377{
378 splash(HZ, "Shutting down");
379 power_off();
380 while(1);
381}
382
383void reboot(void)
384{
385 splash(HZ, "Rebooting");
386 system_reboot();
387 while(1);
388}
389
390/* TODO: clean this up, make the installer generic as well */
391enum {
392 INSTALL,
393 BACKUP,
394 RESTORE,
395};
396
397#ifdef FIIO_M3K
398void bootloader_action(int which)
399{
400 if(init_disk() != 0) {
401 splash2(5*HZ, "Install aborted", "Cannot access SD card");
402 return;
403 }
404
405 const char* msg;
406 switch(which) {
407 case INSTALL: msg = "Installing"; break;
408 case BACKUP: msg = "Backing up"; break;
409 case RESTORE: msg = "Restoring"; break;
410 default: return; /* can't happen */
411 }
412
413 splash(0, msg);
414
415 int rc;
416 switch(which) {
417 case INSTALL: rc = install_boot("/bootloader.m3k"); break;
418 case BACKUP: rc = backup_boot("/fiiom3k-boot.bin"); break;
419 case RESTORE: rc = restore_boot("/fiiom3k-boot.bin"); break;
420 default: return;
421 }
422
423 static char buf[64];
424 snprintf(buf, sizeof(buf), "Failed! Error: %d", rc);
425 const char* msg1 = rc == 0 ? "Success" : buf;
426 const char* msg2 = "Press " BL_QUIT_NAME " to continue";
427 splash2(0, msg1, msg2);
428
429 button_clear_queue();
430 while(button_get(true) != BL_QUIT);
431}
432#else
433void bootloader_action(int which)
434{
435 (void)which;
436 splash(5*HZ, "Not implemented!");
437}
438#endif
439
440void bootloader_install(void)
441{
442 bootloader_action(INSTALL);
443}
444
445void bootloader_backup(void)
446{
447 bootloader_action(BACKUP);
448}
449
450void bootloader_restore(void)
451{
452 bootloader_action(RESTORE);
453}
454
455void main(void)
456{
457 system_init();
458 core_allocator_init();
459 kernel_init();
460 i2c_init();
461 power_init();
462 button_init();
463 enable_irq();
464
465 if(storage_init() < 0) {
466 splash(5*HZ, "storage_init() failed");
467 power_off();
468 }
469
470 filesystem_init();
471
472 /* If USB booting, the user probably needs to enter recovery mode;
473 * let's not force them to hold down the recovery key. */
474 bool recovery_mode = get_boot_flag(BOOT_FLAG_USB_BOOT);
475
476#ifdef HAVE_BUTTON_DATA
477 int bdata;
478 if(button_read_device(&bdata) & BL_RECOVERY)
479#else
480 if(button_read_device() & BL_RECOVERY)
481#endif
482 recovery_mode = true;
483
484 /* If boot fails, it will return and continue on below */
485 if(!recovery_mode)
486 boot_rockbox();
487
488 /* This function does not return. */
489 recovery_menu();
490}