From 3f26fcf34001197ed267fa1ad549095aae49c88e Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Tue, 11 May 2021 13:28:43 +0100 Subject: 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 --- bootloader/SOURCES | 2 +- bootloader/fiiom3k.c | 264 ++++++++++++++-- firmware/SOURCES | 8 +- firmware/export/installer.h | 31 -- firmware/target/mips/ingenic_x1000/boot.lds | 6 +- firmware/target/mips/ingenic_x1000/crt0.S | 33 -- .../target/mips/ingenic_x1000/fiiom3k/boot.make | 30 ++ .../mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c | 345 +++++++++++++-------- .../mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h | 51 +++ .../mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 38 ++- .../target/mips/ingenic_x1000/fiiom3k/spl-target.h | 29 -- .../target/mips/ingenic_x1000/nand-x1000-err.h | 27 -- firmware/target/mips/ingenic_x1000/nand-x1000.c | 7 +- firmware/target/mips/ingenic_x1000/nand-x1000.h | 9 +- firmware/target/mips/ingenic_x1000/spl-start.S | 97 ++++++ .../target/mips/ingenic_x1000/spl-x1000-defs.h | 66 ---- firmware/target/mips/ingenic_x1000/spl-x1000.c | 141 ++++----- firmware/target/mips/ingenic_x1000/spl-x1000.h | 11 +- firmware/target/mips/ingenic_x1000/spl.lds | 9 +- firmware/target/mips/ingenic_x1000/x1000boot.make | 53 ++++ tools/configure | 21 +- tools/root.make | 2 + 22 files changed, 790 insertions(+), 490 deletions(-) delete mode 100644 firmware/export/installer.h create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/boot.make create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h delete mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h delete mode 100644 firmware/target/mips/ingenic_x1000/nand-x1000-err.h create mode 100644 firmware/target/mips/ingenic_x1000/spl-start.S delete mode 100644 firmware/target/mips/ingenic_x1000/spl-x1000-defs.h create mode 100644 firmware/target/mips/ingenic_x1000/x1000boot.make diff --git a/bootloader/SOURCES b/bootloader/SOURCES index ff37971a8d..57c23b115c 100644 --- a/bootloader/SOURCES +++ b/bootloader/SOURCES @@ -89,6 +89,6 @@ show_logo.c #elif defined(SANSA_CONNECT) sansaconnect.c show_logo.c -#elif defined(FIIO_M3K) && !defined(BOOTLOADER_SPL) +#elif defined(FIIO_M3K) fiiom3k.c #endif diff --git a/bootloader/fiiom3k.c b/bootloader/fiiom3k.c index 9f169755ae..3d42ba7314 100644 --- a/bootloader/fiiom3k.c +++ b/bootloader/fiiom3k.c @@ -20,6 +20,7 @@ ****************************************************************************/ #include "system.h" +#include "core_alloc.h" #include "kernel/kernel-internal.h" #include "i2c.h" #include "power.h" @@ -35,6 +36,9 @@ #include "rb-loader.h" #include "loader_strerror.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 */ extern unsigned char loadaddress[]; @@ -59,6 +63,7 @@ void exec(void* dst, const void* src, int bytes) static bool lcd_inited = false; static bool usb_inited = false; +static bool disk_inited = false; static void init_lcd(void) { @@ -79,6 +84,12 @@ static void init_lcd(void) 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) { 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_putsxy((LCD_WIDTH - (SYSFONT_WIDTH * strlen(rbversion))) / 2, - (LCD_HEIGHT - SYSFONT_HEIGHT), rbversion); + put_version(); lcd_update(); sleep(delay); } @@ -109,20 +119,232 @@ static void do_usb(void) usb_inited = true; } - do_splash(0, "Waiting for USB"); + do_splash2(0, "Waiting for USB", "Press POWER to go back"); - while(button_get(true) != SYS_USB_CONNECTED); - do_splash(0, "USB mode"); + int btn; + while(1) { + btn = button_get(true); + if(btn == SYS_USB_CONNECTED) + break; + else if(btn == BUTTON_POWER) + return; + } + do_splash(0, "USB mode"); usb_acknowledge(SYS_USB_CONNECTED_ACK); while(button_get(true) != SYS_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) { + 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(); + core_allocator_init(); kernel_init(); i2c_init(); power_init(); @@ -136,32 +358,12 @@ void main(void) filesystem_init(); - int loadsize = 0; - do { - if(!storage_present(0)) { - do_splash(HZ, "Insert SD card"); - continue; - } - - if(disk_mount_all() <= 0) { - 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(button_read_device() & BUTTON_VOL_UP) + recovery_mode = true; - if(lcd_inited) - backlight_hw_off(); + if(!recovery_mode) + do_boot(); - disable_irq(); - - exec(loadaddress, loadbuffer, loadsize); + /* If boot fails or user holds Vol+, go to recovery menu */ + recovery_menu(); } diff --git a/firmware/SOURCES b/firmware/SOURCES index 8995e6a5b4..052847a6a6 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1664,9 +1664,9 @@ target/mips/ingenic_x1000/msc-x1000.c #if (CONFIG_STORAGE & STORAGE_SD) target/mips/ingenic_x1000/sd-x1000.c #endif -#ifdef BOOTLOADER_SPL +target/mips/ingenic_x1000/spl-start.S target/mips/ingenic_x1000/spl-x1000.c -#endif +common/ucl_decompress.c #endif /* CONFIG_CPU == X1000 */ #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/backlight-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/power-fiiom3k.c -#ifdef BOOTLOADER_SPL target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c +#ifdef BOOTLOADER +target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c #endif #endif /* FIIO_M3K */ diff --git a/firmware/export/installer.h b/firmware/export/installer.h deleted file mode 100644 index 802798618d..0000000000 --- a/firmware/export/installer.h +++ /dev/null @@ -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_ */ diff --git a/firmware/target/mips/ingenic_x1000/boot.lds b/firmware/target/mips/ingenic_x1000/boot.lds index 81468a95fc..c274d69aab 100644 --- a/firmware/target/mips/ingenic_x1000/boot.lds +++ b/firmware/target/mips/ingenic_x1000/boot.lds @@ -1,5 +1 @@ -#ifdef BOOTLOADER_SPL -# include "spl.lds" -#else -# include "app.lds" -#endif +#include "app.lds" diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S index b717f96692..b36500b462 100644 --- a/firmware/target/mips/ingenic_x1000/crt0.S +++ b/firmware/target/mips/ingenic_x1000/crt0.S @@ -34,33 +34,6 @@ .section .init.text _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 @@ -80,7 +53,6 @@ _cache_loop: mtc0 v0, C0_Config, 7 nop -#ifndef BOOTLOADER_SPL /* Copy IRAM from BSS to low memory. */ la t0, _iramcopy la t1, _iramstart @@ -91,7 +63,6 @@ _iram_loop: addiu t0, 4 bne t1, t2, _iram_loop sw t3, -4(t1) -#endif /* Clear the BSS segment (needed to zero-initialize C static values) */ la t0, _bssbegin @@ -103,7 +74,6 @@ _bss_loop: sw zero, -4(t0) _bss_done: -#ifndef BOOTLOADER_SPL /* Set stack pointer and clear the stack */ la sp, stackend la t0, stackbegin @@ -120,13 +90,11 @@ _irqstack_loop: addiu t0, 4 bne t0, k0, _irqstack_loop sw t1, -4(t0) -#endif /* Jump to C code */ j main nop -#ifndef BOOTLOADER_SPL /* Exception entry points */ .section .vectors.1, "ax", %progbits j tlb_refill_handler @@ -260,6 +228,5 @@ _exception_return: lw sp, 0x80(sp) eret nop -#endif .set pop diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make b/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make new file mode 100644 index 0000000000..77b23167c2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make @@ -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)/%,%,$^) diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c index 154785ee0b..10a58ace38 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c @@ -19,192 +19,265 @@ * ****************************************************************************/ -#include "installer.h" +#include "installer-fiiom3k.h" #include "nand-x1000.h" #include "system.h" #include "core_alloc.h" #include "file.h" +#include "microtar.h" +#include +#include +#include -#define INSTALL_SUCCESS 0 -#define ERR_FLASH_OPEN_FAILED (-1) -#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 install_from_buffer(const void* buf) +#define IMAGE_SIZE (128 * 1024) +#define TAR_SIZE (256 * 1024) + +static int flash_prepare(void) { - int status = INSTALL_SUCCESS; int mf_id, dev_id; + int rc; + + rc = nand_open(); + if(rc < 0) + return INSTALL_ERR_FLASH(NAND_OPEN, rc); - if(nand_open()) - return ERR_FLASH_OPEN_FAILED; - if(nand_identify(&mf_id, &dev_id)) { - status = ERR_FLASH_OPEN_FAILED; - goto _exit; + rc = nand_identify(&mf_id, &dev_id); + if(rc < 0) { + nand_close(); + return INSTALL_ERR_FLASH(NAND_IDENTIFY, rc); } - if(nand_enable_writes(true)) { - status = ERR_FLASH_DISABLE_WP_FAILED; - goto _exit; + return INSTALL_SUCCESS; +} + +static void flash_finish(void) +{ + /* Ensure writes are always disabled when we finish. + * Errors are safe to ignore here, there's nothing we could do anyway. */ + nand_enable_writes(false); + nand_close(); +} + +static int flash_img_read(uint8_t* buffer) +{ + int rc = flash_prepare(); + if(rc < 0) + goto error; + + rc = nand_read(0, IMAGE_SIZE, buffer); + if(rc < 0) { + rc = INSTALL_ERR_FLASH(NAND_READ, rc); + goto error; } - if(nand_erase(0, BOOT_IMAGE_SIZE)) { - status = ERR_FLASH_ERASE_FAILED; - goto _exit; + 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; } - if(nand_write(0, BOOT_IMAGE_SIZE, (const uint8_t*)buf)) { - status = ERR_FLASH_WRITE_FAILED; - goto _exit; + rc = nand_erase(0, IMAGE_SIZE); + if(rc < 0) { + rc = INSTALL_ERR_FLASH(NAND_ERASE, rc); + goto error; } - if(nand_enable_writes(false)) { - status = ERR_FLASH_ENABLE_WP_FAILED; - goto _exit; + rc = nand_write(0, IMAGE_SIZE, buffer); + if(rc < 0) { + rc = INSTALL_ERR_FLASH(NAND_WRITE, rc); + goto error; } - _exit: - nand_close(); - return status; + error: + flash_finish(); + return rc; } -static int dump_to_buffer(void* buf) +static int patch_img(mtar_t* tar, uint8_t* buffer, const char* filename, + size_t patch_offset, size_t patch_size) { - int status = INSTALL_SUCCESS; - int mf_id, dev_id; - - if(nand_open()) - return ERR_FLASH_OPEN_FAILED; - if(nand_identify(&mf_id, &dev_id)) { - status = ERR_FLASH_OPEN_FAILED; - goto _exit; + /* 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; } - if(nand_read(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) { - status = ERR_FLASH_READ_FAILED; - goto _exit; + /* 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; } - _exit: - nand_close(); - return status; + return INSTALL_SUCCESS; } -int install_bootloader(const char* path) +int install_boot(const char* srcfile) { - /* Allocate memory to hold image */ - size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1; - int handle = core_alloc("boot_image", bufsize); - if(handle < 0) - return ERR_OUT_OF_MEMORY; - - int status = INSTALL_SUCCESS; - void* buffer = core_get_data(handle); - CACHEALIGN_BUFFER(buffer, bufsize); + int rc; + mtar_t* tar = NULL; + int handle = -1; - /* Open the boot image */ - int fd = open(path, O_RDONLY); - if(fd < 0) { - status = ERR_CANNOT_READ_FILE; - goto _exit; + /* 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; } - /* Check file size */ - off_t fsize = filesize(fd); - if(fsize != BOOT_IMAGE_SIZE) { - status = ERR_WRONG_SIZE; - goto _exit; - } + 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); + + /* Read the flash -- we need an existing image to patch */ + rc = flash_img_read(buffer); + if(rc < 0) + goto error; - /* Read the file into the buffer */ - ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE); - if(cnt != BOOT_IMAGE_SIZE) { - status = ERR_CANNOT_READ_FILE; - goto _exit; + /* Open the tarball */ + rc = mtar_open(tar, srcfile, "r"); + if(rc != MTAR_ESUCCESS) { + rc = INSTALL_ERR_MTAR(TAR_OPEN, rc); + goto error; } - /* Perform the installation */ - status = install_from_buffer(buffer); + /* Extract the needed files & patch 'em in */ + rc = patch_img(tar, buffer, "spl.m3k", 0, 12 * 1024); + if(rc < 0) + goto error; - _exit: - if(fd >= 0) - close(fd); - core_free(handle); - return status; + rc = patch_img(tar, buffer, "bootloader.ucl", 0x6800, 102 * 1024); + if(rc < 0) + goto error; + + /* Flash the new image */ + rc = flash_img_write(buffer); + if(rc < 0) + goto error; + + rc = INSTALL_SUCCESS; + + error: + if(tar && tar->close) + mtar_close(tar); + if(handle >= 0) + core_free(handle); + return rc; } -/* Dump the current bootloader to a file */ -int dump_bootloader(const char* path) +int backup_boot(const char* destfile) { - /* Allocate memory to hold image */ - size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1; - int handle = core_alloc("boot_image", bufsize); - if(handle < 0) - return -1; - - /* Read data from flash */ + int rc; + int handle = -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); - int status = dump_to_buffer(buffer); - if(status) - goto _exit; - /* Open file */ - fd = open(path, O_CREAT|O_TRUNC|O_WRONLY); + rc = flash_img_read(buffer); + if(rc < 0) + goto error; + + fd = open(destfile, O_CREAT|O_TRUNC|O_WRONLY); if(fd < 0) { - status = ERR_CANNOT_WRITE_FILE; - goto _exit; + rc = INSTALL_ERR_FILE_IO; + goto error; } - /* Write data to file */ - ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE); - if(cnt != BOOT_IMAGE_SIZE) { - status = ERR_CANNOT_WRITE_FILE; - goto _exit; + ssize_t cnt = write(fd, buffer, IMAGE_SIZE); + if(cnt != IMAGE_SIZE) { + rc = INSTALL_ERR_FILE_IO; + goto error; } - _exit: + error: if(fd >= 0) close(fd); - core_free(handle); - return status; + if(handle >= 0) + core_free(handle); + return rc; } -const char* installer_strerror(int rc) +int restore_boot(const char* srcfile) { - switch(rc) { - case INSTALL_SUCCESS: - return "Success"; - case ERR_FLASH_OPEN_FAILED: - return "Can't open flash device"; - case ERR_FLASH_ENABLE_WP_FAILED: - return "Couldn't re-enable write protect"; - case ERR_FLASH_DISABLE_WP_FAILED: - 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"; + int rc; + int handle = -1; + int fd = -1; + 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); + + 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; } diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h new file mode 100644 index 0000000000..eb700e6689 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h @@ -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 + +#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__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c index bdd0ffc2e0..efea5aa323 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c @@ -25,6 +25,11 @@ #include "system.h" #include +/* 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 * before the choice will be accepted. Currently 100ms. */ @@ -56,7 +61,7 @@ const struct spl_boot_option spl_boot_options[] = { */ .nand_addr = 0x6800, .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, .cmdline = NULL, }, @@ -80,7 +85,7 @@ const struct spl_boot_option spl_boot_options[] = { void spl_error(void) { - const int pin = (1 << 24); + const uint32_t pin = (1 << 24); /* Turn on button light */ 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 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 */ gpio_config(GPIO_A, pinmask, GPIO_INPUT); @@ -116,19 +125,18 @@ int spl_get_boot_option(void) if(pin != lastpin) { /* This will always be set on the first iteration */ deadline = __ost_read32() + BTN_STABLE_TIME; + iter_count += 1; } - } while(__ost_read32() < deadline); - - /* Play button boots original firmware */ - if(pin == (1 << 17)) - return SPL_BOOTOPT_ORIG_FW; - - /* Volume up boots recovery */ - if(pin == (1 << 19)) - return SPL_BOOTOPT_RECOVERY; + } while(iter_count < max_iter_count && __ost_read32() < deadline); - /* Default is to boot Rockbox */ - return SPL_BOOTOPT_ROCKBOX; + if(iter_count < max_iter_count && (pin & (1 << 17))) { + if(pin & (1 << 19)) + return BOOTOPTION_RECOVERY; /* Play+Volume Up */ + else + return BOOTOPTION_ORIG_FW; /* Play */ + } else { + return BOOTOPTION_ROCKBOX; /* Volume Up or no buttons */ + } } 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 * ---- * 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 * 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) | 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 */ clk_set_ddr(X1000_CLK_SCLK_A, 5); diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h deleted file mode 100644 index ac90508f44..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h +++ /dev/null @@ -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__ */ diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h deleted file mode 100644 index 869fe73ac9..0000000000 --- a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h +++ /dev/null @@ -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__ */ diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index 1770324fb3..fbac824789 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c @@ -200,11 +200,8 @@ static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) return NAND_SUCCESS; if(write && !nand_drv.write_enabled) return NAND_ERR_WRITE_PROTECT; - /* FIXME: re-enable this check after merging new SPL+bootloader. - * It's only necessary for DMA, which is currently not used, but it's a - * good practice anyway. Disable for now due to SPL complications. */ - /*if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) - return NAND_ERR_UNALIGNED;*/ + if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) + return NAND_ERR_UNALIGNED; addr >>= nand_drv.chip_data->log2_page_size; size >>= nand_drv.chip_data->log2_page_size; diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h index 6c415b1170..f5db0bbfa5 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.h +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h @@ -36,7 +36,14 @@ #include #include #include -#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 */ #define NANDCHIP_FLAG_QUAD 0x01 diff --git a/firmware/target/mips/ingenic_x1000/spl-start.S b/firmware/target/mips/ingenic_x1000/spl-start.S new file mode 100644 index 0000000000..58346fe750 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-start.S @@ -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 diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h b/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h deleted file mode 100644 index 1d9f120ee2..0000000000 --- a/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h +++ /dev/null @@ -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 - -#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__ */ diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c index 6505cabbef..07453f6182 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.c +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c @@ -20,7 +20,6 @@ ****************************************************************************/ #include "spl-x1000.h" -#include "spl-target.h" #include "clk-x1000.h" #include "nand-x1000.h" #include "system.h" @@ -29,16 +28,15 @@ #include "x1000/ddrc.h" #include "x1000/ddrc_apb.h" #include "x1000/ddrphy.h" +#include "ucl_decompress.h" -struct x1000_spl_arguments* const spl_arguments = - (struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS; - -struct x1000_spl_status* const spl_status = - (struct x1000_spl_status*)SPL_STATUS_ADDRESS; - -/* defined to be Linux compatible; Rockbox needs no arguments so there - * is no harm in passing them and we save a little code size */ -typedef void(*entry_fn)(int, char**, int, int); +#ifdef FIIO_M3K +# define SPL_DDR_MEMORYSIZE 64 +# define SPL_DDR_AUTOSR_EN 1 +# define SPL_DDR_NEED_BYPASS 1 +#else +# error "please add SPL memory definitions" +#endif /* 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 @@ -243,83 +241,70 @@ static int nandread(uint32_t addr, uint32_t size, void* buffer) return rc; } -static int nandwrite(uint32_t addr, uint32_t size, const void* buffer) -{ - int rc; - int mf_id, dev_id; +/* Entry point function type, defined to be Linux compatible. */ +typedef void(*entry_fn)(int, char**, int, int); - if((rc = nand_open())) - return rc; - if((rc = nand_identify(&mf_id, &dev_id))) { - nand_close(); - return rc; - } +/* Kernel command line arguments */ +static char* argv[2]; - if((rc = nand_enable_writes(true))) - goto _end; +/* 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; - if((rc = nand_erase(addr, size))) - goto _end1; +void spl_main(void) +{ + int opt_index; + uint8_t* load_addr; + const struct spl_boot_option* opt; - rc = nand_write(addr, size, (const uint8_t*)buffer); + /* Basic hardware init */ + init(); - _end1: - /* an error here is very unlikely, so ignore it */ - nand_enable_writes(false); + /* If doing a USB boot, host PC will upload 2nd stage itself, + * we should not load anything from flash or change clocks. */ + if((boot_sel & 3) == 2) + return; - _end: - nand_close(); - return rc; -} + /* Get the boot option */ + opt_index = spl_get_boot_option(); + opt = &spl_boot_options[opt_index]; + load_addr = (uint8_t*)opt->load_addr; -/* Kernel command line arguments */ -static char* argv[2]; + /* 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(); -void main(void) -{ - if(!(SPL_ARGUMENTS->flags & SPL_FLAG_SKIP_INIT)) - init(); - - switch(SPL_ARGUMENTS->command) { - case SPL_CMD_BOOT: { - int option = SPL_ARGUMENTS->param1; - if(option == SPL_BOOTOPT_CHOOSE) - option = spl_get_boot_option(); - if(option == SPL_BOOTOPT_NONE) - return; - - const struct spl_boot_option* opt = &spl_boot_options[option-1]; - if(nandread(opt->nand_addr, opt->nand_size, (void*)opt->load_addr)) + if(!opt->cmdline) { + /* 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(); - - /* Let target handle necessary pre-boot setup */ - spl_handle_pre_boot(option); - - /* Reading the Linux command line from the bootloader is handled by - * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. - * - * 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; - argv[0] = 0; - argv[1] = (char*)opt->cmdline; - - commit_discard_idcache(); - entry(2, argv, 0, 0); - __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; - } + /* Reading the Linux command line from the bootloader is handled by + * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. It's + * simply an (int argc, char* argv[]) thing. + */ + entry_fn entry = (entry_fn)opt->exec_addr; + argv[0] = 0; + argv[1] = (char*)opt->cmdline; + + commit_discard_idcache(); + entry(2, argv, 0, 0); + __builtin_unreachable(); } diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h index d2255a8d05..062cb40f88 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.h +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h @@ -22,10 +22,7 @@ #ifndef __SPL_X1000_H__ #define __SPL_X1000_H__ -#include "spl-x1000-defs.h" - -#define SPL_ARGUMENTS ((struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS) -#define SPL_STATUS ((struct x1000_spl_status*)SPL_STATUS_ADDRESS) +#include struct spl_boot_option { uint32_t nand_addr; @@ -35,15 +32,13 @@ struct spl_boot_option { 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[]; /* Called on a fatal error */ extern void spl_error(void) __attribute__((noreturn)); -/* When SPL boots with SPL_BOOTOPTION_CHOOSE, this function is invoked - * to let the target figure out the boot option based on buttons the - * user is pressing */ +/* Invoked by SPL main routine to determine the boot option */ extern int spl_get_boot_option(void); /* Do any setup/initialization needed for the given boot option, this diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds index 36ae8c34d1..e932bd9c2e 100644 --- a/firmware/target/mips/ingenic_x1000/spl.lds +++ b/firmware/target/mips/ingenic_x1000/spl.lds @@ -3,8 +3,8 @@ OUTPUT_FORMAT("elf32-littlemips") OUTPUT_ARCH(MIPS) -ENTRY(_start) -STARTUP(target/mips/ingenic_x1000/crt0.o) +ENTRY(_spl_start) +STARTUP(target/mips/ingenic_x1000/spl-start.o) MEMORY { /* First 4k of TCSM is used by mask ROM for stack + variables, @@ -15,9 +15,12 @@ MEMORY { SECTIONS { + /* Mask ROM variables, addresses found by disassembly */ + boot_sel = X1000_TCSM_BASE + 0x1ec; + .text : { - *(.init.text); + *(.init.spl); *(.text*); *(.icode*); } > TCSM diff --git a/firmware/target/mips/ingenic_x1000/x1000boot.make b/firmware/target/mips/ingenic_x1000/x1000boot.make new file mode 100644 index 0000000000..e69b6a1439 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/x1000boot.make @@ -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,$<,$@) diff --git a/tools/configure b/tools/configure index f1aa03fd08..9539dc687c 100755 --- a/tools/configure +++ b/tools/configure @@ -1624,7 +1624,8 @@ fi genericbitmaptools="$toolset bmp2rb" # scramble is used by all other targets scramblebitmaptools="$genericbitmaptools scramble" - + # used by X1000 targets + x1000tools="$genericbitmaptools scramble mkspl-x1000 uclpack" # ---- For each target ---- # @@ -4100,15 +4101,13 @@ fi appextra="recorder:gui" plugins="yes" tool="$rootdir/tools/scramble -add=fiiom3k " - boottool="$rootdir/tools/scramble -add=fiiom3k " - spltool="$rootdir/tools/mkspl-x1000 -type=nand -ppb=2 -bpp=2 " + boottool="" # not used output="rockbox.m3k" bootoutput="bootloader.m3k" - sploutput="spl.m3k" sysfontbl="16-Terminus" # toolset is the tools within the tools directory that we build for # this particular target. - toolset="$toolset mkspl-x1000" + toolset="$x1000tools" bmp2rb_mono="$rootdir/tools/bmp2rb -f 0" bmp2rb_native="$rootdir/tools/bmp2rb -f 4" # architecture, manufacturer and model for the target-tree build @@ -4187,9 +4186,6 @@ case $modelname in sansae200) gdbstub=", (E)raser" ;; - fiiom3k) - gdbstub=", (X) SPL loader" - ;; *) ;; esac @@ -4215,15 +4211,6 @@ fi bootloader="1" 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]) appsdir='$(ROOTDIR)/bootloader' apps="bootloader" diff --git a/tools/root.make b/tools/root.make index 70f81c0165..22834b3324 100644 --- a/tools/root.make +++ b/tools/root.make @@ -109,6 +109,8 @@ ifneq (,$(findstring bootloader,$(APPSDIR))) include $(ROOTDIR)/firmware/target/hosted/aigo/erosq.make else ifneq (,$(findstring fiio,$(APP_TYPE))) include $(ROOTDIR)/firmware/target/hosted/fiio/fiio.make + else ifneq (,$(findstring ingenic_x1000,$(MANUFACTURER))) + include $(ROOTDIR)/firmware/target/mips/ingenic_x1000/x1000boot.make else include $(APPSDIR)/bootloader.make endif -- cgit v1.2.3