summaryrefslogtreecommitdiff
path: root/bootloader/x1000.c
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 /bootloader/x1000.c
parentd6dcb996847b547e1e980884fbc06966293a7f6a (diff)
downloadrockbox-e9d228832ccbee7e486c45a51680c6f94ce1ab92.tar.gz
rockbox-e9d228832ccbee7e486c45a51680c6f94ce1ab92.zip
x1000: Unified bootloader
Change-Id: Ib1f2ca2a376866c61dd1bd62abd6e31210d11e5c
Diffstat (limited to 'bootloader/x1000.c')
-rw-r--r--bootloader/x1000.c490
1 files changed, 490 insertions, 0 deletions
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}