forked from len0rd/rockbox
FiiO M3K: New bootloader
SPL and UCL-compressed bootloader are now packed into one output, bootloader.m3k, eliminating the separate SPL build phase. The Rockbox bootloader now has a recovery menu, accessible by holding VOL+ when booting, that lets you back up, restore, and update the bootloader from the device. Change-Id: I642c6e5fb83587a013ab2fbfd1adab439561ced2
This commit is contained in:
parent
cc22df198d
commit
3f26fcf340
22 changed files with 799 additions and 499 deletions
|
@ -89,6 +89,6 @@ show_logo.c
|
||||||
#elif defined(SANSA_CONNECT)
|
#elif defined(SANSA_CONNECT)
|
||||||
sansaconnect.c
|
sansaconnect.c
|
||||||
show_logo.c
|
show_logo.c
|
||||||
#elif defined(FIIO_M3K) && !defined(BOOTLOADER_SPL)
|
#elif defined(FIIO_M3K)
|
||||||
fiiom3k.c
|
fiiom3k.c
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "core_alloc.h"
|
||||||
#include "kernel/kernel-internal.h"
|
#include "kernel/kernel-internal.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
@ -35,6 +36,9 @@
|
||||||
#include "rb-loader.h"
|
#include "rb-loader.h"
|
||||||
#include "loader_strerror.h"
|
#include "loader_strerror.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "installer-fiiom3k.h"
|
||||||
|
#include "spl-x1000.h"
|
||||||
|
#include "x1000/cpm.h"
|
||||||
|
|
||||||
/* Load address where the binary needs to be placed */
|
/* Load address where the binary needs to be placed */
|
||||||
extern unsigned char loadaddress[];
|
extern unsigned char loadaddress[];
|
||||||
|
@ -59,6 +63,7 @@ void exec(void* dst, const void* src, int bytes)
|
||||||
|
|
||||||
static bool lcd_inited = false;
|
static bool lcd_inited = false;
|
||||||
static bool usb_inited = false;
|
static bool usb_inited = false;
|
||||||
|
static bool disk_inited = false;
|
||||||
|
|
||||||
static void init_lcd(void)
|
static void init_lcd(void)
|
||||||
{
|
{
|
||||||
|
@ -79,6 +84,12 @@ static void init_lcd(void)
|
||||||
lcd_inited = true;
|
lcd_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void put_version(void)
|
||||||
|
{
|
||||||
|
lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2,
|
||||||
|
(LCD_HEIGHT - SYSFONT_HEIGHT), rbversion);
|
||||||
|
}
|
||||||
|
|
||||||
static void do_splash2(int delay, const char* msg, const char* msg2)
|
static void do_splash2(int delay, const char* msg, const char* msg2)
|
||||||
{
|
{
|
||||||
init_lcd();
|
init_lcd();
|
||||||
|
@ -90,8 +101,7 @@ static void do_splash2(int delay, const char* msg, const char* msg2)
|
||||||
(LCD_HEIGHT + 2*SYSFONT_HEIGHT) / 2, msg2);
|
(LCD_HEIGHT + 2*SYSFONT_HEIGHT) / 2, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
lcd_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2,
|
put_version();
|
||||||
(LCD_HEIGHT - SYSFONT_HEIGHT), rbversion);
|
|
||||||
lcd_update();
|
lcd_update();
|
||||||
sleep(delay);
|
sleep(delay);
|
||||||
}
|
}
|
||||||
|
@ -109,20 +119,232 @@ static void do_usb(void)
|
||||||
usb_inited = true;
|
usb_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
do_splash(0, "Waiting for USB");
|
do_splash2(0, "Waiting for USB", "Press POWER to go back");
|
||||||
|
|
||||||
|
int btn;
|
||||||
|
while(1) {
|
||||||
|
btn = button_get(true);
|
||||||
|
if(btn == SYS_USB_CONNECTED)
|
||||||
|
break;
|
||||||
|
else if(btn == BUTTON_POWER)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while(button_get(true) != SYS_USB_CONNECTED);
|
|
||||||
do_splash(0, "USB mode");
|
do_splash(0, "USB mode");
|
||||||
|
|
||||||
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
usb_acknowledge(SYS_USB_CONNECTED_ACK);
|
||||||
while(button_get(true) != SYS_USB_DISCONNECTED);
|
while(button_get(true) != SYS_USB_DISCONNECTED);
|
||||||
|
|
||||||
do_splash(3*HZ, "USB disconnected");
|
do_splash(3*HZ, "USB disconnected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int init_disk(void)
|
||||||
|
{
|
||||||
|
if(disk_inited)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while(!storage_present(0)) {
|
||||||
|
do_splash2(0, "Insert SD card", "Press POWER for recovery");
|
||||||
|
int btn = button_get_w_tmo(HZ);
|
||||||
|
if(btn == BUTTON_POWER)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(disk_mount_all() <= 0) {
|
||||||
|
do_splash(5*HZ, "Cannot mount filesystem");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_inited = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_boot(void)
|
||||||
|
{
|
||||||
|
if(init_disk() != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
|
||||||
|
if(loadsize <= 0) {
|
||||||
|
do_splash2(5*HZ, "Error loading Rockbox",
|
||||||
|
loader_strerror(loadsize));
|
||||||
|
do_usb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lcd_inited)
|
||||||
|
backlight_hw_off();
|
||||||
|
|
||||||
|
disable_irq();
|
||||||
|
exec(loadaddress, loadbuffer, loadsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INSTALL 0
|
||||||
|
#define BACKUP 1
|
||||||
|
#define RESTORE 2
|
||||||
|
|
||||||
|
static void do_install(int which)
|
||||||
|
{
|
||||||
|
int rc = init_disk();
|
||||||
|
if(rc != 0) {
|
||||||
|
do_splash2(5*HZ, "Install aborted", "No SD card present");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* msg;
|
||||||
|
if(rc == INSTALL)
|
||||||
|
msg = "Installing";
|
||||||
|
else if(rc == BACKUP)
|
||||||
|
msg = "Backing up";
|
||||||
|
else
|
||||||
|
msg = "Restoring backup";
|
||||||
|
|
||||||
|
do_splash(0, msg);
|
||||||
|
|
||||||
|
if(which == INSTALL)
|
||||||
|
rc = install_boot("/bootloader.m3k");
|
||||||
|
else if(which == BACKUP)
|
||||||
|
rc = backup_boot("/fiiom3k-boot.bin");
|
||||||
|
else
|
||||||
|
rc = restore_boot("/fiiom3k-boot.bin");
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "Failed! Error: %d", rc);
|
||||||
|
const char* msg1 = rc == 0 ? "Success" : buf;
|
||||||
|
const char* msg2 = "Press POWER to continue";
|
||||||
|
do_splash2(0, msg1, msg2);
|
||||||
|
|
||||||
|
button_clear_queue();
|
||||||
|
while(button_get(true) != BUTTON_POWER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recovery_menu(void)
|
||||||
|
{
|
||||||
|
static const char* items[] = {
|
||||||
|
"--- Rockbox recovery menu ---",
|
||||||
|
"[System]",
|
||||||
|
" Start Rockbox",
|
||||||
|
" USB mode",
|
||||||
|
" Shutdown",
|
||||||
|
" Reboot",
|
||||||
|
"[Bootloader]",
|
||||||
|
" Install or update",
|
||||||
|
" Backup",
|
||||||
|
" Restore",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"VOL+/VOL- move cursor",
|
||||||
|
"PLAY select item",
|
||||||
|
"POWER power off",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int nitems = sizeof(items) / sizeof(char*);
|
||||||
|
|
||||||
|
init_lcd();
|
||||||
|
|
||||||
|
int selection = 2;
|
||||||
|
do {
|
||||||
|
/* Draw menu */
|
||||||
|
lcd_clear_display();
|
||||||
|
|
||||||
|
for(int i = 0; i < nitems; ++i)
|
||||||
|
lcd_puts(0, i, items[i]);
|
||||||
|
|
||||||
|
if(items[selection][0] == ' ')
|
||||||
|
lcd_puts(0, selection, "=>");
|
||||||
|
|
||||||
|
put_version();
|
||||||
|
lcd_update();
|
||||||
|
|
||||||
|
/* Clear queue to avoid accidental input */
|
||||||
|
button_clear_queue();
|
||||||
|
|
||||||
|
/* Get the button */
|
||||||
|
int btn = button_get(true);
|
||||||
|
|
||||||
|
/* Process user input */
|
||||||
|
if(btn == BUTTON_VOL_UP) {
|
||||||
|
for(int i = selection-1; i >= 0; --i) {
|
||||||
|
if(items[i][0] == ' ') {
|
||||||
|
selection = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if(btn == BUTTON_VOL_DOWN) {
|
||||||
|
for(int i = selection+1; i < nitems; ++i) {
|
||||||
|
if(items[i][0] == ' ') {
|
||||||
|
selection = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if(btn == BUTTON_POWER) {
|
||||||
|
selection = 4; /* Shutdown */
|
||||||
|
} else if(btn != BUTTON_PLAY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* User pressed PLAY so decide what action to take */
|
||||||
|
switch(selection) {
|
||||||
|
case 2: /* Start rockbox */
|
||||||
|
do_boot();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: /* USB mode */
|
||||||
|
do_usb();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: /* Shutdown */
|
||||||
|
do_splash(HZ, "Shutting down");
|
||||||
|
power_off();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5: /* Reboot */
|
||||||
|
do_splash(HZ, "Rebooting");
|
||||||
|
system_reboot();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7: /* Install bootloader */
|
||||||
|
do_install(INSTALL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8: /* Backup bootloader */
|
||||||
|
do_install(BACKUP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 9: /* Restore bootloader */
|
||||||
|
do_install(RESTORE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(1);
|
||||||
|
}
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
|
bool recovery_mode = false;
|
||||||
|
|
||||||
|
/* This hack is needed because when USB booting, we cannot initialize
|
||||||
|
* clocks in the SPL -- it may break the mask ROM's USB code. So if the
|
||||||
|
* SPL has not already initialized the clocks, we need to do that now.
|
||||||
|
*
|
||||||
|
* Also use this as a sign that we should enter the recovery menu since
|
||||||
|
* this is probably the expected result if the user is USB booting...
|
||||||
|
*/
|
||||||
|
if(jz_readf(CPM_MPCR, ENABLE)) {
|
||||||
|
spl_handle_pre_boot(0);
|
||||||
|
recovery_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
system_init();
|
system_init();
|
||||||
|
core_allocator_init();
|
||||||
kernel_init();
|
kernel_init();
|
||||||
i2c_init();
|
i2c_init();
|
||||||
power_init();
|
power_init();
|
||||||
|
@ -136,32 +358,12 @@ void main(void)
|
||||||
|
|
||||||
filesystem_init();
|
filesystem_init();
|
||||||
|
|
||||||
int loadsize = 0;
|
if(button_read_device() & BUTTON_VOL_UP)
|
||||||
do {
|
recovery_mode = true;
|
||||||
if(!storage_present(0)) {
|
|
||||||
do_splash(HZ, "Insert SD card");
|
if(!recovery_mode)
|
||||||
continue;
|
do_boot();
|
||||||
}
|
|
||||||
|
/* If boot fails or user holds Vol+, go to recovery menu */
|
||||||
if(disk_mount_all() <= 0) {
|
recovery_menu();
|
||||||
do_splash(5*HZ, "Cannot mount filesystem");
|
|
||||||
do_usb();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
|
|
||||||
if(loadsize <= 0) {
|
|
||||||
do_splash2(5*HZ, "Error loading Rockbox",
|
|
||||||
loader_strerror(loadsize));
|
|
||||||
do_usb();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} while(loadsize <= 0);
|
|
||||||
|
|
||||||
if(lcd_inited)
|
|
||||||
backlight_hw_off();
|
|
||||||
|
|
||||||
disable_irq();
|
|
||||||
|
|
||||||
exec(loadaddress, loadbuffer, loadsize);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1664,9 +1664,9 @@ target/mips/ingenic_x1000/msc-x1000.c
|
||||||
#if (CONFIG_STORAGE & STORAGE_SD)
|
#if (CONFIG_STORAGE & STORAGE_SD)
|
||||||
target/mips/ingenic_x1000/sd-x1000.c
|
target/mips/ingenic_x1000/sd-x1000.c
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOOTLOADER_SPL
|
target/mips/ingenic_x1000/spl-start.S
|
||||||
target/mips/ingenic_x1000/spl-x1000.c
|
target/mips/ingenic_x1000/spl-x1000.c
|
||||||
#endif
|
common/ucl_decompress.c
|
||||||
#endif /* CONFIG_CPU == X1000 */
|
#endif /* CONFIG_CPU == X1000 */
|
||||||
|
|
||||||
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
|
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
|
||||||
|
@ -1696,11 +1696,11 @@ target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c
|
||||||
target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
|
target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
|
||||||
target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
|
target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
|
||||||
target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
|
target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
|
||||||
target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
|
|
||||||
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
|
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
|
||||||
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
|
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
|
||||||
#ifdef BOOTLOADER_SPL
|
|
||||||
target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
|
target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
|
||||||
|
#ifdef BOOTLOADER
|
||||||
|
target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
|
||||||
#endif
|
#endif
|
||||||
#endif /* FIIO_M3K */
|
#endif /* FIIO_M3K */
|
||||||
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
/***************************************************************************
|
|
||||||
* __________ __ ___.
|
|
||||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
||||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
||||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
||||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
||||||
* \/ \/ \/ \/ \/
|
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Aidan MacDonald
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
||||||
* KIND, either express or implied.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef _INSTALLER_H_
|
|
||||||
#define _INSTALLER_H_
|
|
||||||
|
|
||||||
/* Provisional interface for installing/dumping a bootloader */
|
|
||||||
|
|
||||||
extern int install_bootloader(const char* path);
|
|
||||||
extern int dump_bootloader(const char* path);
|
|
||||||
extern const char* installer_strerror(int rc);
|
|
||||||
|
|
||||||
#endif /* _INSTALLER_H_ */
|
|
|
@ -1,5 +1 @@
|
||||||
#ifdef BOOTLOADER_SPL
|
|
||||||
# include "spl.lds"
|
|
||||||
#else
|
|
||||||
#include "app.lds"
|
#include "app.lds"
|
||||||
#endif
|
|
||||||
|
|
|
@ -34,33 +34,6 @@
|
||||||
.section .init.text
|
.section .init.text
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
/* Clear data watchpoint */
|
|
||||||
mtc0 zero, C0_WATCHLO
|
|
||||||
mtc0 zero, C0_WATCHHI
|
|
||||||
|
|
||||||
/* Set BEV, ERL, mask interrupts */
|
|
||||||
li v0, 0x40fc04
|
|
||||||
mtc0 v0, C0_Status
|
|
||||||
|
|
||||||
/* Set Cause_IV to 1 (use special interrupt vector) */
|
|
||||||
li v0, M_CauseIV
|
|
||||||
mtc0 v0, C0_Cause
|
|
||||||
|
|
||||||
/* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */
|
|
||||||
lui v0, 0xb000
|
|
||||||
lw v1, 0x24(v0)
|
|
||||||
ori v1, v1, 0x22
|
|
||||||
sw v1, 0x24(v0)
|
|
||||||
|
|
||||||
/* Enable kseg0 cacheability */
|
|
||||||
li v0, 3
|
|
||||||
mtc0 v0, C0_Config
|
|
||||||
nop
|
|
||||||
|
|
||||||
/* According to ingenic: "enable idx-store-data cache insn" */
|
|
||||||
li v0, 0x20000000
|
|
||||||
mtc0 v0, C0_ErrCtl
|
|
||||||
|
|
||||||
/* Cache init */
|
/* Cache init */
|
||||||
li v0, 0x80000000
|
li v0, 0x80000000
|
||||||
ori v1, v0, 0x4000
|
ori v1, v0, 0x4000
|
||||||
|
@ -80,7 +53,6 @@ _cache_loop:
|
||||||
mtc0 v0, C0_Config, 7
|
mtc0 v0, C0_Config, 7
|
||||||
nop
|
nop
|
||||||
|
|
||||||
#ifndef BOOTLOADER_SPL
|
|
||||||
/* Copy IRAM from BSS to low memory. */
|
/* Copy IRAM from BSS to low memory. */
|
||||||
la t0, _iramcopy
|
la t0, _iramcopy
|
||||||
la t1, _iramstart
|
la t1, _iramstart
|
||||||
|
@ -91,7 +63,6 @@ _iram_loop:
|
||||||
addiu t0, 4
|
addiu t0, 4
|
||||||
bne t1, t2, _iram_loop
|
bne t1, t2, _iram_loop
|
||||||
sw t3, -4(t1)
|
sw t3, -4(t1)
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Clear the BSS segment (needed to zero-initialize C static values) */
|
/* Clear the BSS segment (needed to zero-initialize C static values) */
|
||||||
la t0, _bssbegin
|
la t0, _bssbegin
|
||||||
|
@ -103,7 +74,6 @@ _bss_loop:
|
||||||
sw zero, -4(t0)
|
sw zero, -4(t0)
|
||||||
_bss_done:
|
_bss_done:
|
||||||
|
|
||||||
#ifndef BOOTLOADER_SPL
|
|
||||||
/* Set stack pointer and clear the stack */
|
/* Set stack pointer and clear the stack */
|
||||||
la sp, stackend
|
la sp, stackend
|
||||||
la t0, stackbegin
|
la t0, stackbegin
|
||||||
|
@ -120,13 +90,11 @@ _irqstack_loop:
|
||||||
addiu t0, 4
|
addiu t0, 4
|
||||||
bne t0, k0, _irqstack_loop
|
bne t0, k0, _irqstack_loop
|
||||||
sw t1, -4(t0)
|
sw t1, -4(t0)
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Jump to C code */
|
/* Jump to C code */
|
||||||
j main
|
j main
|
||||||
nop
|
nop
|
||||||
|
|
||||||
#ifndef BOOTLOADER_SPL
|
|
||||||
/* Exception entry points */
|
/* Exception entry points */
|
||||||
.section .vectors.1, "ax", %progbits
|
.section .vectors.1, "ax", %progbits
|
||||||
j tlb_refill_handler
|
j tlb_refill_handler
|
||||||
|
@ -260,6 +228,5 @@ _exception_return:
|
||||||
lw sp, 0x80(sp)
|
lw sp, 0x80(sp)
|
||||||
eret
|
eret
|
||||||
nop
|
nop
|
||||||
#endif
|
|
||||||
|
|
||||||
.set pop
|
.set pop
|
||||||
|
|
30
firmware/target/mips/ingenic_x1000/fiiom3k/boot.make
Normal file
30
firmware/target/mips/ingenic_x1000/fiiom3k/boot.make
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# __________ __ ___.
|
||||||
|
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
# \/ \/ \/ \/ \/
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(ROOTDIR)/lib/microtar/microtar.make
|
||||||
|
|
||||||
|
.SECONDEXPANSION:
|
||||||
|
|
||||||
|
$(BUILDDIR)/spl.m3k: $(BUILDDIR)/spl.bin
|
||||||
|
$(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@
|
||||||
|
|
||||||
|
$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin
|
||||||
|
$(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null
|
||||||
|
|
||||||
|
.PHONY: $(BUILDDIR)/bootloader-info.txt
|
||||||
|
$(BUILDDIR)/bootloader-info.txt:
|
||||||
|
$(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@
|
||||||
|
|
||||||
|
$(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.m3k \
|
||||||
|
$(BUILDDIR)/bootloader.ucl \
|
||||||
|
$(BUILDDIR)/bootloader-info.txt
|
||||||
|
$(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \
|
||||||
|
--numeric-owner --no-acls --no-xattrs --no-selinux \
|
||||||
|
--mode=0644 --owner=0 --group=0 \
|
||||||
|
-cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^)
|
|
@ -19,192 +19,265 @@
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "installer.h"
|
#include "installer-fiiom3k.h"
|
||||||
#include "nand-x1000.h"
|
#include "nand-x1000.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "core_alloc.h"
|
#include "core_alloc.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
#include "microtar.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define INSTALL_SUCCESS 0
|
#define IMAGE_SIZE (128 * 1024)
|
||||||
#define ERR_FLASH_OPEN_FAILED (-1)
|
#define TAR_SIZE (256 * 1024)
|
||||||
#define ERR_FLASH_ENABLE_WP_FAILED (-2)
|
|
||||||
#define ERR_FLASH_DISABLE_WP_FAILED (-3)
|
|
||||||
#define ERR_FLASH_ERASE_FAILED (-4)
|
|
||||||
#define ERR_FLASH_WRITE_FAILED (-5)
|
|
||||||
#define ERR_FLASH_READ_FAILED (-6)
|
|
||||||
#define ERR_OUT_OF_MEMORY (-7)
|
|
||||||
#define ERR_CANNOT_READ_FILE (-8)
|
|
||||||
#define ERR_CANNOT_WRITE_FILE (-9)
|
|
||||||
#define ERR_WRONG_SIZE (-10)
|
|
||||||
|
|
||||||
#define BOOT_IMAGE_SIZE (128 * 1024)
|
static int flash_prepare(void)
|
||||||
|
|
||||||
static int install_from_buffer(const void* buf)
|
|
||||||
{
|
{
|
||||||
int status = INSTALL_SUCCESS;
|
|
||||||
int mf_id, dev_id;
|
int mf_id, dev_id;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if(nand_open())
|
rc = nand_open();
|
||||||
return ERR_FLASH_OPEN_FAILED;
|
if(rc < 0)
|
||||||
if(nand_identify(&mf_id, &dev_id)) {
|
return INSTALL_ERR_FLASH(NAND_OPEN, rc);
|
||||||
status = ERR_FLASH_OPEN_FAILED;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nand_enable_writes(true)) {
|
rc = nand_identify(&mf_id, &dev_id);
|
||||||
status = ERR_FLASH_DISABLE_WP_FAILED;
|
if(rc < 0) {
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nand_erase(0, BOOT_IMAGE_SIZE)) {
|
|
||||||
status = ERR_FLASH_ERASE_FAILED;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nand_write(0, BOOT_IMAGE_SIZE, (const uint8_t*)buf)) {
|
|
||||||
status = ERR_FLASH_WRITE_FAILED;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nand_enable_writes(false)) {
|
|
||||||
status = ERR_FLASH_ENABLE_WP_FAILED;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
_exit:
|
|
||||||
nand_close();
|
nand_close();
|
||||||
return status;
|
return INSTALL_ERR_FLASH(NAND_IDENTIFY, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dump_to_buffer(void* buf)
|
return INSTALL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flash_finish(void)
|
||||||
{
|
{
|
||||||
int status = INSTALL_SUCCESS;
|
/* Ensure writes are always disabled when we finish.
|
||||||
int mf_id, dev_id;
|
* Errors are safe to ignore here, there's nothing we could do anyway. */
|
||||||
|
nand_enable_writes(false);
|
||||||
if(nand_open())
|
|
||||||
return ERR_FLASH_OPEN_FAILED;
|
|
||||||
if(nand_identify(&mf_id, &dev_id)) {
|
|
||||||
status = ERR_FLASH_OPEN_FAILED;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nand_read(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) {
|
|
||||||
status = ERR_FLASH_READ_FAILED;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
_exit:
|
|
||||||
nand_close();
|
nand_close();
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int install_bootloader(const char* path)
|
static int flash_img_read(uint8_t* buffer)
|
||||||
{
|
{
|
||||||
/* Allocate memory to hold image */
|
int rc = flash_prepare();
|
||||||
size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1;
|
if(rc < 0)
|
||||||
int handle = core_alloc("boot_image", bufsize);
|
goto error;
|
||||||
if(handle < 0)
|
|
||||||
return ERR_OUT_OF_MEMORY;
|
|
||||||
|
|
||||||
int status = INSTALL_SUCCESS;
|
rc = nand_read(0, IMAGE_SIZE, buffer);
|
||||||
void* buffer = core_get_data(handle);
|
if(rc < 0) {
|
||||||
|
rc = INSTALL_ERR_FLASH(NAND_READ, rc);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
flash_finish();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flash_img_write(const uint8_t* buffer)
|
||||||
|
{
|
||||||
|
int rc = flash_prepare();
|
||||||
|
if(rc < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
rc = nand_enable_writes(true);
|
||||||
|
if(rc < 0) {
|
||||||
|
rc = INSTALL_ERR_FLASH(NAND_ENABLE_WRITES, rc);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = nand_erase(0, IMAGE_SIZE);
|
||||||
|
if(rc < 0) {
|
||||||
|
rc = INSTALL_ERR_FLASH(NAND_ERASE, rc);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = nand_write(0, IMAGE_SIZE, buffer);
|
||||||
|
if(rc < 0) {
|
||||||
|
rc = INSTALL_ERR_FLASH(NAND_WRITE, rc);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
flash_finish();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int patch_img(mtar_t* tar, uint8_t* buffer, const char* filename,
|
||||||
|
size_t patch_offset, size_t patch_size)
|
||||||
|
{
|
||||||
|
/* Seek to file */
|
||||||
|
mtar_header_t h;
|
||||||
|
int rc = mtar_find(tar, filename, &h);
|
||||||
|
if(rc != MTAR_ESUCCESS) {
|
||||||
|
rc = INSTALL_ERR_MTAR(TAR_FIND, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need a normal file */
|
||||||
|
if(h.type != 0 && h.type != MTAR_TREG)
|
||||||
|
return INSTALL_ERR_BAD_FORMAT;
|
||||||
|
|
||||||
|
/* Check size does not exceed patch area */
|
||||||
|
if(h.size > patch_size)
|
||||||
|
return INSTALL_ERR_BAD_FORMAT;
|
||||||
|
|
||||||
|
/* Read data directly into patch area, fill unused bytes with 0xff */
|
||||||
|
memset(&buffer[patch_offset], 0xff, patch_size);
|
||||||
|
rc = mtar_read_data(tar, &buffer[patch_offset], h.size);
|
||||||
|
if(rc != MTAR_ESUCCESS) {
|
||||||
|
rc = INSTALL_ERR_MTAR(TAR_READ, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return INSTALL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int install_boot(const char* srcfile)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
mtar_t* tar = NULL;
|
||||||
|
int handle = -1;
|
||||||
|
|
||||||
|
/* Allocate enough memory for image and tar state */
|
||||||
|
size_t bufsize = IMAGE_SIZE + sizeof(mtar_t) + 2*CACHEALIGN_SIZE;
|
||||||
|
handle = core_alloc("boot_image", bufsize);
|
||||||
|
if(handle < 0) {
|
||||||
|
rc = INSTALL_ERR_OUT_OF_MEMORY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* buffer = core_get_data(handle);
|
||||||
|
|
||||||
|
/* Tar state alloc */
|
||||||
|
CACHEALIGN_BUFFER(buffer, bufsize);
|
||||||
|
tar = (mtar_t*)buffer;
|
||||||
|
memset(tar, 0, sizeof(tar));
|
||||||
|
|
||||||
|
/* Image buffer alloc */
|
||||||
|
buffer += sizeof(mtar_t);
|
||||||
CACHEALIGN_BUFFER(buffer, bufsize);
|
CACHEALIGN_BUFFER(buffer, bufsize);
|
||||||
|
|
||||||
/* Open the boot image */
|
/* Read the flash -- we need an existing image to patch */
|
||||||
int fd = open(path, O_RDONLY);
|
rc = flash_img_read(buffer);
|
||||||
if(fd < 0) {
|
if(rc < 0)
|
||||||
status = ERR_CANNOT_READ_FILE;
|
goto error;
|
||||||
goto _exit;
|
|
||||||
|
/* Open the tarball */
|
||||||
|
rc = mtar_open(tar, srcfile, "r");
|
||||||
|
if(rc != MTAR_ESUCCESS) {
|
||||||
|
rc = INSTALL_ERR_MTAR(TAR_OPEN, rc);
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check file size */
|
/* Extract the needed files & patch 'em in */
|
||||||
off_t fsize = filesize(fd);
|
rc = patch_img(tar, buffer, "spl.m3k", 0, 12 * 1024);
|
||||||
if(fsize != BOOT_IMAGE_SIZE) {
|
if(rc < 0)
|
||||||
status = ERR_WRONG_SIZE;
|
goto error;
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read the file into the buffer */
|
rc = patch_img(tar, buffer, "bootloader.ucl", 0x6800, 102 * 1024);
|
||||||
ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE);
|
if(rc < 0)
|
||||||
if(cnt != BOOT_IMAGE_SIZE) {
|
goto error;
|
||||||
status = ERR_CANNOT_READ_FILE;
|
|
||||||
goto _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform the installation */
|
/* Flash the new image */
|
||||||
status = install_from_buffer(buffer);
|
rc = flash_img_write(buffer);
|
||||||
|
if(rc < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
_exit:
|
rc = INSTALL_SUCCESS;
|
||||||
if(fd >= 0)
|
|
||||||
close(fd);
|
error:
|
||||||
|
if(tar && tar->close)
|
||||||
|
mtar_close(tar);
|
||||||
|
if(handle >= 0)
|
||||||
core_free(handle);
|
core_free(handle);
|
||||||
return status;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dump the current bootloader to a file */
|
int backup_boot(const char* destfile)
|
||||||
int dump_bootloader(const char* path)
|
|
||||||
{
|
{
|
||||||
/* Allocate memory to hold image */
|
int rc;
|
||||||
size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1;
|
int handle = -1;
|
||||||
int handle = core_alloc("boot_image", bufsize);
|
|
||||||
if(handle < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Read data from flash */
|
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
void* buffer = core_get_data(handle);
|
size_t bufsize = IMAGE_SIZE + CACHEALIGN_SIZE - 1;
|
||||||
|
handle = core_alloc("boot_image", bufsize);
|
||||||
|
if(handle < 0) {
|
||||||
|
rc = INSTALL_ERR_OUT_OF_MEMORY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* buffer = core_get_data(handle);
|
||||||
CACHEALIGN_BUFFER(buffer, bufsize);
|
CACHEALIGN_BUFFER(buffer, bufsize);
|
||||||
int status = dump_to_buffer(buffer);
|
|
||||||
if(status)
|
|
||||||
goto _exit;
|
|
||||||
|
|
||||||
/* Open file */
|
rc = flash_img_read(buffer);
|
||||||
fd = open(path, O_CREAT|O_TRUNC|O_WRONLY);
|
if(rc < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
fd = open(destfile, O_CREAT|O_TRUNC|O_WRONLY);
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
status = ERR_CANNOT_WRITE_FILE;
|
rc = INSTALL_ERR_FILE_IO;
|
||||||
goto _exit;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write data to file */
|
ssize_t cnt = write(fd, buffer, IMAGE_SIZE);
|
||||||
ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE);
|
if(cnt != IMAGE_SIZE) {
|
||||||
if(cnt != BOOT_IMAGE_SIZE) {
|
rc = INSTALL_ERR_FILE_IO;
|
||||||
status = ERR_CANNOT_WRITE_FILE;
|
goto error;
|
||||||
goto _exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_exit:
|
error:
|
||||||
if(fd >= 0)
|
if(fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
if(handle >= 0)
|
||||||
core_free(handle);
|
core_free(handle);
|
||||||
return status;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* installer_strerror(int rc)
|
int restore_boot(const char* srcfile)
|
||||||
{
|
{
|
||||||
switch(rc) {
|
int rc;
|
||||||
case INSTALL_SUCCESS:
|
int handle = -1;
|
||||||
return "Success";
|
int fd = -1;
|
||||||
case ERR_FLASH_OPEN_FAILED:
|
size_t bufsize = IMAGE_SIZE + CACHEALIGN_SIZE - 1;
|
||||||
return "Can't open flash device";
|
handle = core_alloc("boot_image", bufsize);
|
||||||
case ERR_FLASH_ENABLE_WP_FAILED:
|
if(handle < 0) {
|
||||||
return "Couldn't re-enable write protect";
|
rc = INSTALL_ERR_OUT_OF_MEMORY;
|
||||||
case ERR_FLASH_DISABLE_WP_FAILED:
|
goto error;
|
||||||
return "Can't disable write protect";
|
|
||||||
case ERR_FLASH_ERASE_FAILED:
|
|
||||||
return "Flash erase failed";
|
|
||||||
case ERR_FLASH_WRITE_FAILED:
|
|
||||||
return "Flash write error";
|
|
||||||
case ERR_FLASH_READ_FAILED:
|
|
||||||
return "Flash read error";
|
|
||||||
case ERR_OUT_OF_MEMORY:
|
|
||||||
return "Out of memory";
|
|
||||||
case ERR_CANNOT_READ_FILE:
|
|
||||||
return "Error reading file";
|
|
||||||
case ERR_CANNOT_WRITE_FILE:
|
|
||||||
return "Error writing file";
|
|
||||||
case ERR_WRONG_SIZE:
|
|
||||||
return "Wrong file size";
|
|
||||||
default:
|
|
||||||
return "Unknown error";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t* buffer = core_get_data(handle);
|
||||||
|
CACHEALIGN_BUFFER(buffer, bufsize);
|
||||||
|
|
||||||
|
fd = open(srcfile, O_RDONLY);
|
||||||
|
if(fd < 0) {
|
||||||
|
rc = INSTALL_ERR_FILE_NOT_FOUND;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t fsize = filesize(fd);
|
||||||
|
if(fsize != IMAGE_SIZE) {
|
||||||
|
rc = INSTALL_ERR_BAD_FORMAT;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t cnt = read(fd, buffer, IMAGE_SIZE);
|
||||||
|
if(cnt != IMAGE_SIZE) {
|
||||||
|
rc = INSTALL_ERR_FILE_IO;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
|
||||||
|
rc = flash_img_write(buffer);
|
||||||
|
if(rc < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if(fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
if(handle >= 0)
|
||||||
|
core_free(handle);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Aidan MacDonald
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef __INSTALLER_FIIOM3K_H__
|
||||||
|
#define __INSTALLER_FIIOM3K_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define INSTALL_SUCCESS 0
|
||||||
|
#define INSTALL_ERR_OUT_OF_MEMORY (-1)
|
||||||
|
#define INSTALL_ERR_FILE_NOT_FOUND (-2)
|
||||||
|
#define INSTALL_ERR_FILE_IO (-3)
|
||||||
|
#define INSTALL_ERR_BAD_FORMAT (-4)
|
||||||
|
#define INSTALL_ERR_NAND_OPEN (-5)
|
||||||
|
#define INSTALL_ERR_NAND_IDENTIFY (-6)
|
||||||
|
#define INSTALL_ERR_NAND_READ (-7)
|
||||||
|
#define INSTALL_ERR_NAND_ENABLE_WRITES (-8)
|
||||||
|
#define INSTALL_ERR_NAND_ERASE (-9)
|
||||||
|
#define INSTALL_ERR_NAND_WRITE (-10)
|
||||||
|
#define INSTALL_ERR_TAR_OPEN (-11)
|
||||||
|
#define INSTALL_ERR_TAR_FIND (-12)
|
||||||
|
#define INSTALL_ERR_TAR_READ (-13)
|
||||||
|
#define INSTALL_ERR_MTAR(x,y) ((INSTALL_ERR_##x)*100 + (y))
|
||||||
|
#define INSTALL_ERR_FLASH(x,y) ((INSTALL_ERR_##x)*100 + (y))
|
||||||
|
|
||||||
|
/* Install the Rockbox bootloader from a bootloader.m3k image */
|
||||||
|
extern int install_boot(const char* srcfile);
|
||||||
|
|
||||||
|
/* Backup or restore the bootloader from a raw NAND image */
|
||||||
|
extern int backup_boot(const char* destfile);
|
||||||
|
extern int restore_boot(const char* srcfile);
|
||||||
|
|
||||||
|
#endif /* __INSTALLER_FIIOM3K_H__ */
|
|
@ -25,6 +25,11 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Available boot options */
|
||||||
|
#define BOOTOPTION_ROCKBOX 0
|
||||||
|
#define BOOTOPTION_ORIG_FW 1
|
||||||
|
#define BOOTOPTION_RECOVERY 2
|
||||||
|
|
||||||
/* Boot select button state must remain stable for this duration
|
/* Boot select button state must remain stable for this duration
|
||||||
* before the choice will be accepted. Currently 100ms.
|
* before the choice will be accepted. Currently 100ms.
|
||||||
*/
|
*/
|
||||||
|
@ -56,7 +61,7 @@ const struct spl_boot_option spl_boot_options[] = {
|
||||||
*/
|
*/
|
||||||
.nand_addr = 0x6800,
|
.nand_addr = 0x6800,
|
||||||
.nand_size = 0x19800,
|
.nand_size = 0x19800,
|
||||||
.load_addr = X1000_DRAM_BASE - 8, /* first 8 bytes are bootloader ID */
|
.load_addr = X1000_DRAM_END - 0x19800,
|
||||||
.exec_addr = X1000_DRAM_BASE,
|
.exec_addr = X1000_DRAM_BASE,
|
||||||
.cmdline = NULL,
|
.cmdline = NULL,
|
||||||
},
|
},
|
||||||
|
@ -80,7 +85,7 @@ const struct spl_boot_option spl_boot_options[] = {
|
||||||
|
|
||||||
void spl_error(void)
|
void spl_error(void)
|
||||||
{
|
{
|
||||||
const int pin = (1 << 24);
|
const uint32_t pin = (1 << 24);
|
||||||
|
|
||||||
/* Turn on button light */
|
/* Turn on button light */
|
||||||
jz_clr(GPIO_INT(GPIO_C), pin);
|
jz_clr(GPIO_INT(GPIO_C), pin);
|
||||||
|
@ -105,6 +110,10 @@ int spl_get_boot_option(void)
|
||||||
|
|
||||||
uint32_t pin = 1, lastpin = 0;
|
uint32_t pin = 1, lastpin = 0;
|
||||||
uint32_t deadline = 0;
|
uint32_t deadline = 0;
|
||||||
|
/* Iteration count guards against unlikely case of broken buttons
|
||||||
|
* which never stabilize; if this occurs, we always boot Rockbox. */
|
||||||
|
int iter_count = 0;
|
||||||
|
const int max_iter_count = 30;
|
||||||
|
|
||||||
/* Configure the button GPIOs as inputs */
|
/* Configure the button GPIOs as inputs */
|
||||||
gpio_config(GPIO_A, pinmask, GPIO_INPUT);
|
gpio_config(GPIO_A, pinmask, GPIO_INPUT);
|
||||||
|
@ -116,19 +125,18 @@ int spl_get_boot_option(void)
|
||||||
if(pin != lastpin) {
|
if(pin != lastpin) {
|
||||||
/* This will always be set on the first iteration */
|
/* This will always be set on the first iteration */
|
||||||
deadline = __ost_read32() + BTN_STABLE_TIME;
|
deadline = __ost_read32() + BTN_STABLE_TIME;
|
||||||
|
iter_count += 1;
|
||||||
}
|
}
|
||||||
} while(__ost_read32() < deadline);
|
} while(iter_count < max_iter_count && __ost_read32() < deadline);
|
||||||
|
|
||||||
/* Play button boots original firmware */
|
if(iter_count < max_iter_count && (pin & (1 << 17))) {
|
||||||
if(pin == (1 << 17))
|
if(pin & (1 << 19))
|
||||||
return SPL_BOOTOPT_ORIG_FW;
|
return BOOTOPTION_RECOVERY; /* Play+Volume Up */
|
||||||
|
else
|
||||||
/* Volume up boots recovery */
|
return BOOTOPTION_ORIG_FW; /* Play */
|
||||||
if(pin == (1 << 19))
|
} else {
|
||||||
return SPL_BOOTOPT_RECOVERY;
|
return BOOTOPTION_ROCKBOX; /* Volume Up or no buttons */
|
||||||
|
}
|
||||||
/* Default is to boot Rockbox */
|
|
||||||
return SPL_BOOTOPT_ROCKBOX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spl_handle_pre_boot(int bootopt)
|
void spl_handle_pre_boot(int bootopt)
|
||||||
|
@ -145,7 +153,7 @@ void spl_handle_pre_boot(int bootopt)
|
||||||
/* System clock setup -- common to Rockbox and FiiO firmware
|
/* System clock setup -- common to Rockbox and FiiO firmware
|
||||||
* ----
|
* ----
|
||||||
* CPU at 1 GHz, L2 cache at 500 MHz
|
* CPU at 1 GHz, L2 cache at 500 MHz
|
||||||
* AHB0 and AHB2 and 200 MHz
|
* AHB0 and AHB2 at 200 MHz
|
||||||
* PCLK at 100 MHz
|
* PCLK at 100 MHz
|
||||||
* DDR at 200 MHz
|
* DDR at 200 MHz
|
||||||
*/
|
*/
|
||||||
|
@ -153,7 +161,7 @@ void spl_handle_pre_boot(int bootopt)
|
||||||
clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
|
clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
|
||||||
CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
|
CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
|
||||||
|
|
||||||
if(bootopt == SPL_BOOTOPT_ROCKBOX) {
|
if(bootopt == BOOTOPTION_ROCKBOX) {
|
||||||
/* We don't use MPLL in Rockbox, so switch DDR memory to APLL */
|
/* We don't use MPLL in Rockbox, so switch DDR memory to APLL */
|
||||||
clk_set_ddr(X1000_CLK_SCLK_A, 5);
|
clk_set_ddr(X1000_CLK_SCLK_A, 5);
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/***************************************************************************
|
|
||||||
* __________ __ ___.
|
|
||||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
||||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
||||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
||||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
||||||
* \/ \/ \/ \/ \/
|
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Aidan MacDonald
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
||||||
* KIND, either express or implied.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef __SPL_TARGET_H__
|
|
||||||
#define __SPL_TARGET_H__
|
|
||||||
|
|
||||||
#define SPL_DDR_MEMORYSIZE 64
|
|
||||||
#define SPL_DDR_AUTOSR_EN 1
|
|
||||||
#define SPL_DDR_NEED_BYPASS 1
|
|
||||||
|
|
||||||
#endif /* __SPL_TARGET_H__ */
|
|
|
@ -1,27 +0,0 @@
|
||||||
#ifndef __NAND_X1000_ERR_H__
|
|
||||||
#define __NAND_X1000_ERR_H__
|
|
||||||
|
|
||||||
/* Error codes which can be returned by the nand-x1000 API. These codes are
|
|
||||||
* also used by host-side tools, so we define them here to avoid polluting
|
|
||||||
* the namespace with useless X1000 APIs. */
|
|
||||||
#define NANDERR_CHIP_UNSUPPORTED (-1)
|
|
||||||
#define NANDERR_WRITE_PROTECTED (-2)
|
|
||||||
#define NANDERR_UNALIGNED_ADDRESS (-3)
|
|
||||||
#define NANDERR_UNALIGNED_LENGTH (-4)
|
|
||||||
#define NANDERR_READ_FAILED (-5)
|
|
||||||
#define NANDERR_ECC_FAILED (-6)
|
|
||||||
#define NANDERR_ERASE_FAILED (-7)
|
|
||||||
#define NANDERR_PROGRAM_FAILED (-8)
|
|
||||||
#define NANDERR_COMMAND_FAILED (-9)
|
|
||||||
#define NANDERR_OTHER (-99)
|
|
||||||
|
|
||||||
/* TEMPORARY -- compatibility hack for jztool's sake.
|
|
||||||
* This will go away once the new bootloader gets merged */
|
|
||||||
#define NAND_SUCCESS 0
|
|
||||||
#define NAND_ERR_UNKNOWN_CHIP NANDERR_CHIP_UNSUPPORTED
|
|
||||||
#define NAND_ERR_UNALIGNED NANDERR_UNALIGNED_ADDRESS
|
|
||||||
#define NAND_ERR_WRITE_PROTECT NANDERR_WRITE_PROTECTED
|
|
||||||
#define NAND_ERR_CONTROLLER NANDERR_OTHER
|
|
||||||
#define NAND_ERR_COMMAND NANDERR_COMMAND_FAILED
|
|
||||||
|
|
||||||
#endif /* __NAND_X1000_ERR_H__ */
|
|
|
@ -200,11 +200,8 @@ static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf)
|
||||||
return NAND_SUCCESS;
|
return NAND_SUCCESS;
|
||||||
if(write && !nand_drv.write_enabled)
|
if(write && !nand_drv.write_enabled)
|
||||||
return NAND_ERR_WRITE_PROTECT;
|
return NAND_ERR_WRITE_PROTECT;
|
||||||
/* FIXME: re-enable this check after merging new SPL+bootloader.
|
if((uint32_t)buf & (CACHEALIGN_SIZE - 1))
|
||||||
* It's only necessary for DMA, which is currently not used, but it's a
|
return NAND_ERR_UNALIGNED;
|
||||||
* good practice anyway. Disable for now due to SPL complications. */
|
|
||||||
/*if((uint32_t)buf & (CACHEALIGN_SIZE - 1))
|
|
||||||
return NAND_ERR_UNALIGNED;*/
|
|
||||||
|
|
||||||
addr >>= nand_drv.chip_data->log2_page_size;
|
addr >>= nand_drv.chip_data->log2_page_size;
|
||||||
size >>= nand_drv.chip_data->log2_page_size;
|
size >>= nand_drv.chip_data->log2_page_size;
|
||||||
|
|
|
@ -36,7 +36,14 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "nand-x1000-err.h"
|
|
||||||
|
/* Error codes which can be returned by the NAND API */
|
||||||
|
#define NAND_SUCCESS 0
|
||||||
|
#define NAND_ERR_UNKNOWN_CHIP (-1)
|
||||||
|
#define NAND_ERR_UNALIGNED (-2)
|
||||||
|
#define NAND_ERR_WRITE_PROTECT (-3)
|
||||||
|
#define NAND_ERR_CONTROLLER (-4)
|
||||||
|
#define NAND_ERR_COMMAND (-5)
|
||||||
|
|
||||||
/* Chip supports quad I/O for page read/write */
|
/* Chip supports quad I/O for page read/write */
|
||||||
#define NANDCHIP_FLAG_QUAD 0x01
|
#define NANDCHIP_FLAG_QUAD 0x01
|
||||||
|
|
97
firmware/target/mips/ingenic_x1000/spl-start.S
Normal file
97
firmware/target/mips/ingenic_x1000/spl-start.S
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Aidan MacDonald
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "mips.h"
|
||||||
|
|
||||||
|
.text
|
||||||
|
.extern spl_main
|
||||||
|
.global _spl_start
|
||||||
|
|
||||||
|
.set push
|
||||||
|
.set mips32
|
||||||
|
.set noreorder
|
||||||
|
.set noat
|
||||||
|
|
||||||
|
.section .init.spl
|
||||||
|
|
||||||
|
_spl_start:
|
||||||
|
/* Clear data watchpoint */
|
||||||
|
mtc0 zero, C0_WATCHLO
|
||||||
|
mtc0 zero, C0_WATCHHI
|
||||||
|
|
||||||
|
/* Set BEV, ERL, mask interrupts */
|
||||||
|
li v0, 0x40fc04
|
||||||
|
mtc0 v0, C0_Status
|
||||||
|
|
||||||
|
/* Set Cause_IV to 1 (use special interrupt vector) */
|
||||||
|
li v0, M_CauseIV
|
||||||
|
mtc0 v0, C0_Cause
|
||||||
|
|
||||||
|
/* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */
|
||||||
|
lui v0, 0xb000
|
||||||
|
lw v1, 0x24(v0)
|
||||||
|
ori v1, v1, 0x22
|
||||||
|
sw v1, 0x24(v0)
|
||||||
|
|
||||||
|
/* Enable kseg0 cacheability */
|
||||||
|
li v0, 3
|
||||||
|
mtc0 v0, C0_Config
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* According to ingenic: "enable idx-store-data cache insn" */
|
||||||
|
li v0, 0x20000000
|
||||||
|
mtc0 v0, C0_ErrCtl
|
||||||
|
|
||||||
|
/* Cache init */
|
||||||
|
li v0, 0x80000000
|
||||||
|
ori v1, v0, 0x4000
|
||||||
|
mtc0 zero, C0_TAGLO
|
||||||
|
mtc0 zero, C0_TAGHI
|
||||||
|
_cache_loop:
|
||||||
|
cache ICIndexStTag, 0(v0)
|
||||||
|
cache DCIndexStTag, 0(v0)
|
||||||
|
addiu v0, v0, 32
|
||||||
|
bne v0, v1, _cache_loop
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* Invalidate BTB */
|
||||||
|
mfc0 v0, C0_Config, 7
|
||||||
|
nop
|
||||||
|
ori v0, v0, 2
|
||||||
|
mtc0 v0, C0_Config, 7
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* Clear the BSS segment (needed to zero-initialize C static values) */
|
||||||
|
la t0, _bssbegin
|
||||||
|
la t1, _bssend
|
||||||
|
beq t0, t1, _bss_done
|
||||||
|
_bss_loop:
|
||||||
|
addiu t0, 4
|
||||||
|
bne t0, t1, _bss_loop
|
||||||
|
sw zero, -4(t0)
|
||||||
|
_bss_done:
|
||||||
|
|
||||||
|
/* Jump to C code */
|
||||||
|
j spl_main
|
||||||
|
nop
|
||||||
|
|
||||||
|
.set pop
|
|
@ -1,66 +0,0 @@
|
||||||
/***************************************************************************
|
|
||||||
* __________ __ ___.
|
|
||||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
||||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
||||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
||||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
||||||
* \/ \/ \/ \/ \/
|
|
||||||
* $Id$
|
|
||||||
*
|
|
||||||
* Copyright (C) 2021 Aidan MacDonald
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
||||||
* KIND, either express or implied.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef __SPL_X1000_DEFS_H__
|
|
||||||
#define __SPL_X1000_DEFS_H__
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SPL_CMD_BOOT 0
|
|
||||||
#define SPL_CMD_FLASH_READ 1
|
|
||||||
#define SPL_CMD_FLASH_WRITE 2
|
|
||||||
|
|
||||||
#define SPL_BOOTOPT_CHOOSE 0
|
|
||||||
#define SPL_BOOTOPT_ROCKBOX 1
|
|
||||||
#define SPL_BOOTOPT_ORIG_FW 2
|
|
||||||
#define SPL_BOOTOPT_RECOVERY 3
|
|
||||||
#define SPL_BOOTOPT_NONE 4
|
|
||||||
|
|
||||||
#define SPL_FLAG_SKIP_INIT (1 << 0)
|
|
||||||
|
|
||||||
#define SPL_MAX_SIZE (12 * 1024)
|
|
||||||
#define SPL_LOAD_ADDRESS 0xf4001000
|
|
||||||
#define SPL_EXEC_ADDRESS 0xf4001800
|
|
||||||
#define SPL_ARGUMENTS_ADDRESS 0xf40011f0
|
|
||||||
#define SPL_STATUS_ADDRESS 0xf40011e0
|
|
||||||
#define SPL_BUFFER_ADDRESS 0xa0004000
|
|
||||||
|
|
||||||
struct x1000_spl_arguments {
|
|
||||||
uint32_t command;
|
|
||||||
uint32_t param1;
|
|
||||||
uint32_t param2;
|
|
||||||
uint32_t flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct x1000_spl_status {
|
|
||||||
int err_code;
|
|
||||||
int reserved;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SPL_X1000_DEFS_H__ */
|
|
|
@ -20,7 +20,6 @@
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "spl-x1000.h"
|
#include "spl-x1000.h"
|
||||||
#include "spl-target.h"
|
|
||||||
#include "clk-x1000.h"
|
#include "clk-x1000.h"
|
||||||
#include "nand-x1000.h"
|
#include "nand-x1000.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
@ -29,16 +28,15 @@
|
||||||
#include "x1000/ddrc.h"
|
#include "x1000/ddrc.h"
|
||||||
#include "x1000/ddrc_apb.h"
|
#include "x1000/ddrc_apb.h"
|
||||||
#include "x1000/ddrphy.h"
|
#include "x1000/ddrphy.h"
|
||||||
|
#include "ucl_decompress.h"
|
||||||
|
|
||||||
struct x1000_spl_arguments* const spl_arguments =
|
#ifdef FIIO_M3K
|
||||||
(struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS;
|
# define SPL_DDR_MEMORYSIZE 64
|
||||||
|
# define SPL_DDR_AUTOSR_EN 1
|
||||||
struct x1000_spl_status* const spl_status =
|
# define SPL_DDR_NEED_BYPASS 1
|
||||||
(struct x1000_spl_status*)SPL_STATUS_ADDRESS;
|
#else
|
||||||
|
# error "please add SPL memory definitions"
|
||||||
/* defined to be Linux compatible; Rockbox needs no arguments so there
|
#endif
|
||||||
* is no harm in passing them and we save a little code size */
|
|
||||||
typedef void(*entry_fn)(int, char**, int, int);
|
|
||||||
|
|
||||||
/* Note: This is based purely on disassembly of the SPL from the FiiO M3K.
|
/* Note: This is based purely on disassembly of the SPL from the FiiO M3K.
|
||||||
* The code there is somewhat generic and corresponds roughly to Ingenic's
|
* The code there is somewhat generic and corresponds roughly to Ingenic's
|
||||||
|
@ -243,63 +241,64 @@ static int nandread(uint32_t addr, uint32_t size, void* buffer)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nandwrite(uint32_t addr, uint32_t size, const void* buffer)
|
/* Entry point function type, defined to be Linux compatible. */
|
||||||
{
|
typedef void(*entry_fn)(int, char**, int, int);
|
||||||
int rc;
|
|
||||||
int mf_id, dev_id;
|
|
||||||
|
|
||||||
if((rc = nand_open()))
|
|
||||||
return rc;
|
|
||||||
if((rc = nand_identify(&mf_id, &dev_id))) {
|
|
||||||
nand_close();
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((rc = nand_enable_writes(true)))
|
|
||||||
goto _end;
|
|
||||||
|
|
||||||
if((rc = nand_erase(addr, size)))
|
|
||||||
goto _end1;
|
|
||||||
|
|
||||||
rc = nand_write(addr, size, (const uint8_t*)buffer);
|
|
||||||
|
|
||||||
_end1:
|
|
||||||
/* an error here is very unlikely, so ignore it */
|
|
||||||
nand_enable_writes(false);
|
|
||||||
|
|
||||||
_end:
|
|
||||||
nand_close();
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kernel command line arguments */
|
/* Kernel command line arguments */
|
||||||
static char* argv[2];
|
static char* argv[2];
|
||||||
|
|
||||||
void main(void)
|
/* This variable is defined by the maskrom. It's simply the level of the
|
||||||
|
* boot_sel[2:0] pins (GPIOs B28-30) at boot time. Meaning of the bits:
|
||||||
|
*
|
||||||
|
* boot_sel[2] boot_sel[1] boot_sel[0] Description
|
||||||
|
* -----------------------------------------------------------------
|
||||||
|
* 1 X X EXCLK is 26 MHz
|
||||||
|
* 0 X X EXCLK is 24 MHz
|
||||||
|
* X 1 1 Boot from SFC0
|
||||||
|
* X 0 1 Boot from MSC0
|
||||||
|
* X 1 0 Boot from USB 2.0 device
|
||||||
|
* -----------------------------------------------------------------
|
||||||
|
* Source: X1000 PM pg. 687, "XBurst Boot ROM Specification"
|
||||||
|
*/
|
||||||
|
extern const uint32_t boot_sel;
|
||||||
|
|
||||||
|
void spl_main(void)
|
||||||
{
|
{
|
||||||
if(!(SPL_ARGUMENTS->flags & SPL_FLAG_SKIP_INIT))
|
int opt_index;
|
||||||
|
uint8_t* load_addr;
|
||||||
|
const struct spl_boot_option* opt;
|
||||||
|
|
||||||
|
/* Basic hardware init */
|
||||||
init();
|
init();
|
||||||
|
|
||||||
switch(SPL_ARGUMENTS->command) {
|
/* If doing a USB boot, host PC will upload 2nd stage itself,
|
||||||
case SPL_CMD_BOOT: {
|
* we should not load anything from flash or change clocks. */
|
||||||
int option = SPL_ARGUMENTS->param1;
|
if((boot_sel & 3) == 2)
|
||||||
if(option == SPL_BOOTOPT_CHOOSE)
|
|
||||||
option = spl_get_boot_option();
|
|
||||||
if(option == SPL_BOOTOPT_NONE)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const struct spl_boot_option* opt = &spl_boot_options[option-1];
|
/* Get the boot option */
|
||||||
if(nandread(opt->nand_addr, opt->nand_size, (void*)opt->load_addr))
|
opt_index = spl_get_boot_option();
|
||||||
|
opt = &spl_boot_options[opt_index];
|
||||||
|
load_addr = (uint8_t*)opt->load_addr;
|
||||||
|
|
||||||
|
/* Set up hardware, load stuff from flash */
|
||||||
|
spl_handle_pre_boot(opt_index);
|
||||||
|
if(nandread(opt->nand_addr, opt->nand_size, load_addr))
|
||||||
spl_error();
|
spl_error();
|
||||||
|
|
||||||
/* Let target handle necessary pre-boot setup */
|
if(!opt->cmdline) {
|
||||||
spl_handle_pre_boot(option);
|
/* No command line => we are booting Rockbox, decompress bootloader.
|
||||||
|
* In the case of Rockbox, load binary directly to exec address */
|
||||||
|
uint32_t out_size = X1000_DRAM_END - opt->exec_addr;
|
||||||
|
int rc = ucl_unpack(load_addr, opt->nand_size,
|
||||||
|
(uint8_t*)opt->exec_addr, &out_size);
|
||||||
|
if(rc != UCL_E_OK)
|
||||||
|
spl_error();
|
||||||
|
}
|
||||||
|
|
||||||
/* Reading the Linux command line from the bootloader is handled by
|
/* Reading the Linux command line from the bootloader is handled by
|
||||||
* arch/mips/xburst/core/prom.c -- see Ingenic kernel sources.
|
* arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. It's
|
||||||
*
|
* simply an (int argc, char* argv[]) thing.
|
||||||
* Rockbox doesn't use arguments, but passing them does not hurt and it
|
|
||||||
* saves an unnecessary branch.
|
|
||||||
*/
|
*/
|
||||||
entry_fn entry = (entry_fn)opt->exec_addr;
|
entry_fn entry = (entry_fn)opt->exec_addr;
|
||||||
argv[0] = 0;
|
argv[0] = 0;
|
||||||
|
@ -309,17 +308,3 @@ void main(void)
|
||||||
entry(2, argv, 0, 0);
|
entry(2, argv, 0, 0);
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
case SPL_CMD_FLASH_READ:
|
|
||||||
SPL_STATUS->err_code = nandread(SPL_ARGUMENTS->param1,
|
|
||||||
SPL_ARGUMENTS->param2,
|
|
||||||
(void*)SPL_BUFFER_ADDRESS);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case SPL_CMD_FLASH_WRITE:
|
|
||||||
SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1,
|
|
||||||
SPL_ARGUMENTS->param2,
|
|
||||||
(const void*)SPL_BUFFER_ADDRESS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,10 +22,7 @@
|
||||||
#ifndef __SPL_X1000_H__
|
#ifndef __SPL_X1000_H__
|
||||||
#define __SPL_X1000_H__
|
#define __SPL_X1000_H__
|
||||||
|
|
||||||
#include "spl-x1000-defs.h"
|
#include <stdint.h>
|
||||||
|
|
||||||
#define SPL_ARGUMENTS ((struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS)
|
|
||||||
#define SPL_STATUS ((struct x1000_spl_status*)SPL_STATUS_ADDRESS)
|
|
||||||
|
|
||||||
struct spl_boot_option {
|
struct spl_boot_option {
|
||||||
uint32_t nand_addr;
|
uint32_t nand_addr;
|
||||||
|
@ -35,15 +32,13 @@ struct spl_boot_option {
|
||||||
const char* cmdline; /* for Linux */
|
const char* cmdline; /* for Linux */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Defined by target, indices are 0 = ROCKBOX, 1 = ORIG_FW, etc... */
|
/* Defined by target, order is not important */
|
||||||
extern const struct spl_boot_option spl_boot_options[];
|
extern const struct spl_boot_option spl_boot_options[];
|
||||||
|
|
||||||
/* Called on a fatal error */
|
/* Called on a fatal error */
|
||||||
extern void spl_error(void) __attribute__((noreturn));
|
extern void spl_error(void) __attribute__((noreturn));
|
||||||
|
|
||||||
/* When SPL boots with SPL_BOOTOPTION_CHOOSE, this function is invoked
|
/* Invoked by SPL main routine to determine the boot option */
|
||||||
* to let the target figure out the boot option based on buttons the
|
|
||||||
* user is pressing */
|
|
||||||
extern int spl_get_boot_option(void);
|
extern int spl_get_boot_option(void);
|
||||||
|
|
||||||
/* Do any setup/initialization needed for the given boot option, this
|
/* Do any setup/initialization needed for the given boot option, this
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
OUTPUT_FORMAT("elf32-littlemips")
|
OUTPUT_FORMAT("elf32-littlemips")
|
||||||
OUTPUT_ARCH(MIPS)
|
OUTPUT_ARCH(MIPS)
|
||||||
ENTRY(_start)
|
ENTRY(_spl_start)
|
||||||
STARTUP(target/mips/ingenic_x1000/crt0.o)
|
STARTUP(target/mips/ingenic_x1000/spl-start.o)
|
||||||
|
|
||||||
MEMORY {
|
MEMORY {
|
||||||
/* First 4k of TCSM is used by mask ROM for stack + variables,
|
/* First 4k of TCSM is used by mask ROM for stack + variables,
|
||||||
|
@ -15,9 +15,12 @@ MEMORY {
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
|
/* Mask ROM variables, addresses found by disassembly */
|
||||||
|
boot_sel = X1000_TCSM_BASE + 0x1ec;
|
||||||
|
|
||||||
.text :
|
.text :
|
||||||
{
|
{
|
||||||
*(.init.text);
|
*(.init.spl);
|
||||||
*(.text*);
|
*(.text*);
|
||||||
*(.icode*);
|
*(.icode*);
|
||||||
} > TCSM
|
} > TCSM
|
||||||
|
|
53
firmware/target/mips/ingenic_x1000/x1000boot.make
Normal file
53
firmware/target/mips/ingenic_x1000/x1000boot.make
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# __________ __ ___.
|
||||||
|
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
# \/ \/ \/ \/ \/
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
|
||||||
|
INCLUDES += -I$(APPSDIR)
|
||||||
|
SRC += $(call preprocess, $(APPSDIR)/SOURCES)
|
||||||
|
|
||||||
|
LDSDEP := $(FIRMDIR)/export/cpu.h $(FIRMDIR)/export/config/$(MODELNAME).h
|
||||||
|
|
||||||
|
BOOTLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/boot.lds
|
||||||
|
BOOTLINK := $(BUILDDIR)/boot.link
|
||||||
|
|
||||||
|
SPLLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/spl.lds
|
||||||
|
SPLLINK := $(BUILDDIR)/spl.link
|
||||||
|
|
||||||
|
CLEANOBJS += $(BUILDDIR)/bootloader.* $(BUILDDIR)/spl.*
|
||||||
|
|
||||||
|
include $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/$(MODELNAME)/boot.make
|
||||||
|
|
||||||
|
.SECONDEXPANSION:
|
||||||
|
|
||||||
|
$(BOOTLINK): $(BOOTLDS) $(LDSDEP)
|
||||||
|
$(call PRINTS,PP $(@F))
|
||||||
|
$(call preprocess2file,$<,$@,)
|
||||||
|
|
||||||
|
$(BUILDDIR)/bootloader.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(BOOTLINK)
|
||||||
|
$(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \
|
||||||
|
-L$(BUILDDIR)/firmware -lfirmware \
|
||||||
|
-L$(BUILDDIR)/lib $(call a2lnk, $(CORE_LIBS)) \
|
||||||
|
-lgcc -T$(BOOTLINK) $(GLOBAL_LDOPTS) \
|
||||||
|
-Wl,--gc-sections -Wl,-Map,$(BUILDDIR)/bootloader.map
|
||||||
|
|
||||||
|
$(BUILDDIR)/bootloader.bin: $(BUILDDIR)/bootloader.elf
|
||||||
|
$(call PRINTS,OC $(@F))$(call objcopy,$<,$@)
|
||||||
|
|
||||||
|
$(SPLLINK): $(SPLLDS) $(LDSDEP)
|
||||||
|
$(call PRINTS,PP $(@F))
|
||||||
|
$(call preprocess2file,$<,$@,)
|
||||||
|
|
||||||
|
$(BUILDDIR)/spl.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(SPLLINK)
|
||||||
|
$(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \
|
||||||
|
-L$(BUILDDIR)/firmware -lfirmware \
|
||||||
|
-L$(BUILDDIR)/lib $(call a2lnk, $(CORE_LIBS)) \
|
||||||
|
-lgcc -T$(SPLLINK) $(GLOBAL_LDOPTS) \
|
||||||
|
-Wl,--gc-sections -Wl,-Map,$(BUILDDIR)/spl.map
|
||||||
|
|
||||||
|
$(BUILDDIR)/spl.bin: $(BUILDDIR)/spl.elf
|
||||||
|
$(call PRINTS,OC $(@F))$(call objcopy,$<,$@)
|
21
tools/configure
vendored
21
tools/configure
vendored
|
@ -1624,7 +1624,8 @@ fi
|
||||||
genericbitmaptools="$toolset bmp2rb"
|
genericbitmaptools="$toolset bmp2rb"
|
||||||
# scramble is used by all other targets
|
# scramble is used by all other targets
|
||||||
scramblebitmaptools="$genericbitmaptools scramble"
|
scramblebitmaptools="$genericbitmaptools scramble"
|
||||||
|
# used by X1000 targets
|
||||||
|
x1000tools="$genericbitmaptools scramble mkspl-x1000 uclpack"
|
||||||
|
|
||||||
# ---- For each target ----
|
# ---- For each target ----
|
||||||
#
|
#
|
||||||
|
@ -4100,15 +4101,13 @@ fi
|
||||||
appextra="recorder:gui"
|
appextra="recorder:gui"
|
||||||
plugins="yes"
|
plugins="yes"
|
||||||
tool="$rootdir/tools/scramble -add=fiiom3k "
|
tool="$rootdir/tools/scramble -add=fiiom3k "
|
||||||
boottool="$rootdir/tools/scramble -add=fiiom3k "
|
boottool="" # not used
|
||||||
spltool="$rootdir/tools/mkspl-x1000 -type=nand -ppb=2 -bpp=2 "
|
|
||||||
output="rockbox.m3k"
|
output="rockbox.m3k"
|
||||||
bootoutput="bootloader.m3k"
|
bootoutput="bootloader.m3k"
|
||||||
sploutput="spl.m3k"
|
|
||||||
sysfontbl="16-Terminus"
|
sysfontbl="16-Terminus"
|
||||||
# toolset is the tools within the tools directory that we build for
|
# toolset is the tools within the tools directory that we build for
|
||||||
# this particular target.
|
# this particular target.
|
||||||
toolset="$toolset mkspl-x1000"
|
toolset="$x1000tools"
|
||||||
bmp2rb_mono="$rootdir/tools/bmp2rb -f 0"
|
bmp2rb_mono="$rootdir/tools/bmp2rb -f 0"
|
||||||
bmp2rb_native="$rootdir/tools/bmp2rb -f 4"
|
bmp2rb_native="$rootdir/tools/bmp2rb -f 4"
|
||||||
# architecture, manufacturer and model for the target-tree build
|
# architecture, manufacturer and model for the target-tree build
|
||||||
|
@ -4187,9 +4186,6 @@ case $modelname in
|
||||||
sansae200)
|
sansae200)
|
||||||
gdbstub=", (E)raser"
|
gdbstub=", (E)raser"
|
||||||
;;
|
;;
|
||||||
fiiom3k)
|
|
||||||
gdbstub=", (X) SPL loader"
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -4215,15 +4211,6 @@ fi
|
||||||
bootloader="1"
|
bootloader="1"
|
||||||
echo "sansa eraser build selected"
|
echo "sansa eraser build selected"
|
||||||
;;
|
;;
|
||||||
[Xx])
|
|
||||||
appsdir='$(ROOTDIR)/bootloader'
|
|
||||||
apps="bootloader"
|
|
||||||
extradefines="$extradefines -DBOOTLOADER -DBOOTLOADER_SPL -ffunction-sections -fdata-sections"
|
|
||||||
bootloader="1"
|
|
||||||
tool="$spltool"
|
|
||||||
output="$sploutput"
|
|
||||||
echo "SPL bootloader build selected"
|
|
||||||
;;
|
|
||||||
[Bb])
|
[Bb])
|
||||||
appsdir='$(ROOTDIR)/bootloader'
|
appsdir='$(ROOTDIR)/bootloader'
|
||||||
apps="bootloader"
|
apps="bootloader"
|
||||||
|
|
|
@ -109,6 +109,8 @@ ifneq (,$(findstring bootloader,$(APPSDIR)))
|
||||||
include $(ROOTDIR)/firmware/target/hosted/aigo/erosq.make
|
include $(ROOTDIR)/firmware/target/hosted/aigo/erosq.make
|
||||||
else ifneq (,$(findstring fiio,$(APP_TYPE)))
|
else ifneq (,$(findstring fiio,$(APP_TYPE)))
|
||||||
include $(ROOTDIR)/firmware/target/hosted/fiio/fiio.make
|
include $(ROOTDIR)/firmware/target/hosted/fiio/fiio.make
|
||||||
|
else ifneq (,$(findstring ingenic_x1000,$(MANUFACTURER)))
|
||||||
|
include $(ROOTDIR)/firmware/target/mips/ingenic_x1000/x1000boot.make
|
||||||
else
|
else
|
||||||
include $(APPSDIR)/bootloader.make
|
include $(APPSDIR)/bootloader.make
|
||||||
endif
|
endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue