1
0
Fork 0
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:
Aidan MacDonald 2021-05-11 13:28:43 +01:00
parent cc22df198d
commit 3f26fcf340
22 changed files with 799 additions and 499 deletions

View file

@ -1,5 +1 @@
#ifdef BOOTLOADER_SPL
# include "spl.lds"
#else
# include "app.lds"
#endif
#include "app.lds"

View file

@ -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

View 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)/%,%,$^)

View file

@ -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 <stdint.h>
#include <string.h>
#include <stdio.h>
#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 IMAGE_SIZE (128 * 1024)
#define TAR_SIZE (256 * 1024)
#define BOOT_IMAGE_SIZE (128 * 1024)
static int install_from_buffer(const void* buf)
static int flash_prepare(void)
{
int status = INSTALL_SUCCESS;
int mf_id, dev_id;
int 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_open();
if(rc < 0)
return INSTALL_ERR_FLASH(NAND_OPEN, rc);
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;
}
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();
return status;
return INSTALL_SUCCESS;
}
static int dump_to_buffer(void* buf)
static void flash_finish(void)
{
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;
}
if(nand_read(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) {
status = ERR_FLASH_READ_FAILED;
goto _exit;
}
_exit:
/* 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();
return status;
}
int install_bootloader(const char* path)
static int flash_img_read(uint8_t* buffer)
{
/* 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 rc = flash_prepare();
if(rc < 0)
goto error;
int status = INSTALL_SUCCESS;
void* buffer = core_get_data(handle);
rc = nand_read(0, IMAGE_SIZE, buffer);
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);
/* Open the boot image */
int fd = open(path, O_RDONLY);
if(fd < 0) {
status = ERR_CANNOT_READ_FILE;
goto _exit;
/* Read the flash -- we need an existing image to patch */
rc = flash_img_read(buffer);
if(rc < 0)
goto error;
/* 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 */
off_t fsize = filesize(fd);
if(fsize != BOOT_IMAGE_SIZE) {
status = ERR_WRONG_SIZE;
goto _exit;
}
/* Extract the needed files & patch 'em in */
rc = patch_img(tar, buffer, "spl.m3k", 0, 12 * 1024);
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;
}
rc = patch_img(tar, buffer, "bootloader.ucl", 0x6800, 102 * 1024);
if(rc < 0)
goto error;
/* Perform the installation */
status = install_from_buffer(buffer);
/* Flash the new image */
rc = flash_img_write(buffer);
if(rc < 0)
goto error;
_exit:
if(fd >= 0)
close(fd);
core_free(handle);
return status;
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;
}

View file

@ -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__ */

View file

@ -25,6 +25,11 @@
#include "system.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
* 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);
} while(iter_count < max_iter_count && __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;
/* 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);

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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;

View file

@ -36,7 +36,14 @@
#include <stdint.h>
#include <stdbool.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 */
#define NANDCHIP_FLAG_QUAD 0x01

View 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

View file

@ -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__ */

View file

@ -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;
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;
}
/* Entry point function type, defined to be Linux compatible. */
typedef void(*entry_fn)(int, char**, int, int);
/* Kernel command line arguments */
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))
init();
int opt_index;
uint8_t* load_addr;
const struct spl_boot_option* opt;
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;
/* Basic hardware init */
init();
const struct spl_boot_option* opt = &spl_boot_options[option-1];
if(nandread(opt->nand_addr, opt->nand_size, (void*)opt->load_addr))
/* 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;
/* Get the boot option */
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();
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;
/* 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;
case SPL_CMD_FLASH_WRITE:
SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1,
SPL_ARGUMENTS->param2,
(const void*)SPL_BUFFER_ADDRESS);
return;
}
commit_discard_idcache();
entry(2, argv, 0, 0);
__builtin_unreachable();
}

View file

@ -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 <stdint.h>
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

View file

@ -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

View 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,$<,$@)