x1000: Unified flash bootloader installer

Change-Id: Ib1d41d4e7d663ff8a21eb08108c13568f7408533
This commit is contained in:
Aidan MacDonald 2021-07-11 02:32:05 +01:00
parent e9d228832c
commit 84362141a0
6 changed files with 390 additions and 324 deletions

View file

@ -48,16 +48,13 @@
#include "loader_strerror.h"
#include "version.h"
#include "boot-x1000.h"
#include "installer-x1000.h"
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef FIIO_M3K
# include "installer-fiiom3k.h"
#endif
#if defined(FIIO_M3K)
# define BL_RECOVERY BUTTON_VOL_UP
# define BL_UP BUTTON_VOL_UP
@ -68,6 +65,7 @@
# define BL_DOWN_NAME "VOL-"
# define BL_SELECT_NAME "PLAY"
# define BL_QUIT_NAME "POWER"
# define BOOTBACKUP_FILE "/fiiom3k-boot.bin"
#else
# error "Missing keymap!"
#endif
@ -387,14 +385,12 @@ void reboot(void)
while(1);
}
/* TODO: clean this up, make the installer generic as well */
enum {
INSTALL,
BACKUP,
RESTORE,
};
#ifdef FIIO_M3K
void bootloader_action(int which)
{
if(init_disk() != 0) {
@ -414,14 +410,14 @@ void bootloader_action(int which)
int rc;
switch(which) {
case INSTALL: rc = install_boot("/bootloader.m3k"); break;
case BACKUP: rc = backup_boot("/fiiom3k-boot.bin"); break;
case RESTORE: rc = restore_boot("/fiiom3k-boot.bin"); break;
case INSTALL: rc = install_bootloader("/bootloader." BOOTFILE_EXT); break;
case BACKUP: rc = backup_bootloader(BOOTBACKUP_FILE); break;
case RESTORE: rc = restore_bootloader(BOOTBACKUP_FILE); break;
default: return;
}
static char buf[64];
snprintf(buf, sizeof(buf), "Failed! Error: %d", rc);
snprintf(buf, sizeof(buf), "%s (%d)", installer_strerror(rc), rc);
const char* msg1 = rc == 0 ? "Success" : buf;
const char* msg2 = "Press " BL_QUIT_NAME " to continue";
splash2(0, msg1, msg2);
@ -429,13 +425,6 @@ void bootloader_action(int which)
button_clear_queue();
while(button_get(true) != BL_QUIT);
}
#else
void bootloader_action(int which)
{
(void)which;
splash(5*HZ, "Not implemented!");
}
#endif
void bootloader_install(void)
{

View file

@ -1676,9 +1676,12 @@ target/mips/ingenic_x1000/msc-x1000.c
#if (CONFIG_STORAGE & STORAGE_SD)
target/mips/ingenic_x1000/sd-x1000.c
#endif
#ifdef BOOTLOADER
target/mips/ingenic_x1000/installer-x1000.c
target/mips/ingenic_x1000/spl-start.S
target/mips/ingenic_x1000/spl-x1000.c
common/ucl_decompress.c
#endif
#endif /* CONFIG_CPU == X1000 */
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
@ -1711,9 +1714,6 @@ target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
#ifdef BOOTLOADER
target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
#endif
#endif /* FIIO_M3K */
#if defined(LYRE_PROTO1)

View file

@ -1,253 +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.
*
****************************************************************************/
#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 IMAGE_SIZE (128 * 1024)
#define TAR_SIZE (256 * 1024)
static int flash_img_read(uint8_t* buffer)
{
nand_drv* drv = nand_init();
nand_lock(drv);
int rc = nand_open(drv);
if(rc < 0)
goto error;
rc = nand_read_bytes(drv, 0, IMAGE_SIZE, buffer);
if(rc < 0) {
rc = INSTALL_ERR_FLASH(NAND_READ, rc);
goto error;
}
error:
nand_close(drv);
nand_unlock(drv);
return rc;
}
static int flash_img_write(const uint8_t* buffer)
{
nand_drv* drv = nand_init();
nand_lock(drv);
int rc = nand_open(drv);
if(rc < 0)
goto error;
rc = nand_write_bytes(drv, 0, IMAGE_SIZE, buffer);
if(rc < 0) {
rc = INSTALL_ERR_FLASH(NAND_WRITE, rc);
goto error;
}
error:
nand_close(drv);
nand_unlock(drv);
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);
/* 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;
}
/* Extract the needed files & patch 'em in */
rc = patch_img(tar, buffer, "spl.m3k", 0, 12 * 1024);
if(rc < 0)
goto error;
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;
}
int backup_boot(const char* destfile)
{
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);
rc = flash_img_read(buffer);
if(rc < 0)
goto error;
fd = open(destfile, O_CREAT|O_TRUNC|O_WRONLY);
if(fd < 0) {
rc = INSTALL_ERR_FILE_IO;
goto error;
}
ssize_t cnt = write(fd, buffer, IMAGE_SIZE);
if(cnt != IMAGE_SIZE) {
rc = INSTALL_ERR_FILE_IO;
goto error;
}
error:
if(fd >= 0)
close(fd);
if(handle >= 0)
core_free(handle);
return rc;
}
int restore_boot(const char* srcfile)
{
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

@ -1,51 +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_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

@ -0,0 +1,326 @@
/***************************************************************************
* __________ __ ___.
* 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 "installer-x1000.h"
#include "nand-x1000.h"
#include "core_alloc.h"
#include "file.h"
#include "microtar.h"
#include <stddef.h>
struct update_part {
const char* filename;
size_t offset;
size_t length;
};
/* Parts of the flash to update. The offset and length are given in bytes,
* offset relative to start of flash. The region's new contents are given
* by the named file inside the update archive. If any file is missing, the
* update will fail. (gracefully! nothing is written unless the package has
* all its components)
*
* If the update file is smaller than the region size, unused space at the
* end of the region is padded with 0xff.
*
* NOTE: The current code assumes all parts are contiguous. The current
* update map fits in one eraseblock, but if it ever needs extending beyond
* that, better implement a bitmap to indicate which blocks need updating
* and which can be skipped. We don't want to erase and reprogram blocks
* for no good reason, it's bad for the flash lifespan.
*/
static const struct update_part updates[] = {
{
.filename = "spl." BOOTFILE_EXT,
.offset = 0,
.length = 12 * 1024,
},
{
.filename = "bootloader.ucl",
.offset = 0x6800,
.length = 102 * 1024,
},
};
static const int num_updates = sizeof(updates) / sizeof(struct update_part);
/* calculate the offset and length of the update image; this is constant
* for a given target, based on the update parts and the NAND chip geometry.
*/
static void get_image_loc(nand_drv* ndrv, size_t* offptr, size_t* lenptr)
{
size_t blk_size = ndrv->chip->page_size << ndrv->chip->log2_ppb;
size_t img_off = 0;
size_t img_len = 0;
/* calculate minimal image needed to contain all update blocks */
for(int i = 0; i < num_updates; ++i) {
img_len = MAX(img_len, updates[i].offset + updates[i].length);
img_off = MIN(img_off, updates[i].offset);
}
/* round everything to a multiple of the block size */
size_t r_off = blk_size * (img_off / blk_size);
size_t r_len = blk_size * ((img_len + img_off - r_off + blk_size - 1) / blk_size);
*offptr = r_off;
*lenptr = r_len;
}
/* Read in a single part of the update from the tarball, and patch it
* into the image */
static int patch_part(mtar_t* tar, const struct update_part* part,
uint8_t* img_buf, size_t img_off)
{
mtar_header_t h;
int rc = mtar_find(tar, part->filename, &h);
if(rc != MTAR_ESUCCESS)
return IERR_BAD_FORMAT;
if(h.type != 0 && h.type != MTAR_TREG)
return IERR_BAD_FORMAT;
if(h.size > part->length)
return IERR_BAD_FORMAT;
/* wipe the patched area, and read in the new data */
memset(&img_buf[part->offset - img_off], 0xff, part->length);
rc = mtar_read_data(tar, &img_buf[part->offset - img_off], h.size);
if(rc != MTAR_ESUCCESS)
return IERR_FILE_IO;
return IERR_SUCCESS;
}
struct updater {
int buf_hnd; /* core_alloc handle for our memory buffer */
size_t buf_len; /* sizeof the buffer */
uint8_t* img_buf;
size_t img_off; /* image address in flash */
size_t img_len; /* image length in flash = size of the buffer */
mtar_t* tar;
nand_drv* ndrv;
};
static int updater_init(struct updater* u)
{
int rc;
/* initialize stuff correctly */
u->buf_hnd = -1;
u->buf_len = 0;
u->img_buf = NULL;
u->img_off = 0;
u->img_len = 0;
u->tar = NULL;
u->ndrv = NULL;
/* open NAND */
u->ndrv = nand_init();
nand_lock(u->ndrv);
rc = nand_open(u->ndrv);
if(rc != NAND_SUCCESS) {
rc = IERR_NAND_OPEN;
goto error;
}
get_image_loc(u->ndrv, &u->img_off, &u->img_len);
/* buf_len is a bit oversized here, but it's not really important */
u->buf_len = u->img_len + sizeof(mtar_t) + 2*CACHEALIGN_SIZE;
u->buf_hnd = core_alloc("boot_image", u->buf_len);
if(u->buf_hnd < 0) {
rc = IERR_OUT_OF_MEMORY;
goto error;
}
/* allocate from the buffer */
uint8_t* buffer = (uint8_t*)core_get_data(u->buf_hnd);
size_t buf_len = u->buf_len;
CACHEALIGN_BUFFER(buffer, buf_len);
u->img_buf = buffer;
buffer += u->img_len;
buf_len -= u->img_len;
CACHEALIGN_BUFFER(buffer, buf_len);
u->tar = (mtar_t*)buffer;
memset(u->tar, 0, sizeof(struct mtar_t));
rc = IERR_SUCCESS;
error:
return rc;
}
static void updater_cleanup(struct updater* u)
{
if(u->tar && u->tar->close)
mtar_close(u->tar);
if(u->buf_hnd >= 0)
core_free(u->buf_hnd);
if(u->ndrv) {
nand_close(u->ndrv);
nand_unlock(u->ndrv);
}
}
int install_bootloader(const char* filename)
{
struct updater u;
int rc = updater_init(&u);
if(rc != IERR_SUCCESS)
goto error;
/* get the image */
rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
if(rc != NAND_SUCCESS) {
rc = IERR_NAND_READ;
goto error;
}
/* get the tarball */
rc = mtar_open(u.tar, filename, "r");
if(rc != MTAR_ESUCCESS) {
if(rc == MTAR_EOPENFAIL)
rc = IERR_FILE_NOT_FOUND;
else if(rc == MTAR_EREADFAIL)
rc = IERR_FILE_IO;
else
rc = IERR_BAD_FORMAT;
goto error;
}
/* patch stuff */
for(int i = 0; i < num_updates; ++i) {
rc = patch_part(u.tar, &updates[i], u.img_buf, u.img_off);
if(rc != IERR_SUCCESS)
goto error;
}
/* write back the patched image */
rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
if(rc != NAND_SUCCESS) {
rc = IERR_NAND_WRITE;
goto error;
}
rc = IERR_SUCCESS;
error:
updater_cleanup(&u);
return rc;
}
int backup_bootloader(const char* filename)
{
int rc, fd = 0;
struct updater u;
rc = updater_init(&u);
if(rc != IERR_SUCCESS)
goto error;
/* read image */
rc = nand_read_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
if(rc != NAND_SUCCESS) {
rc = IERR_NAND_READ;
goto error;
}
/* write to file */
fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY);
if(fd < 0) {
rc = IERR_FILE_IO;
goto error;
}
ssize_t cnt = write(fd, u.img_buf, u.img_len);
if(cnt < 0 || (size_t)cnt != u.img_len) {
rc = IERR_FILE_IO;
goto error;
}
rc = IERR_SUCCESS;
error:
if(fd >= 0)
close(fd);
updater_cleanup(&u);
return rc;
}
int restore_bootloader(const char* filename)
{
int rc, fd = 0;
struct updater u;
rc = updater_init(&u);
if(rc != IERR_SUCCESS)
goto error;
/* read from file */
fd = open(filename, O_RDONLY);
if(fd < 0) {
rc = IERR_FILE_NOT_FOUND;
goto error;
}
ssize_t cnt = read(fd, u.img_buf, u.img_len);
if(cnt < 0 || (size_t)cnt != u.img_len) {
rc = IERR_FILE_IO;
goto error;
}
/* write image */
rc = nand_write_bytes(u.ndrv, u.img_off, u.img_len, u.img_buf);
if(rc != NAND_SUCCESS) {
rc = IERR_NAND_WRITE;
goto error;
}
rc = IERR_SUCCESS;
error:
if(fd >= 0)
close(fd);
updater_cleanup(&u);
return rc;
}
const char* installer_strerror(int rc)
{
switch(rc) {
case IERR_SUCCESS: return "Success";
case IERR_OUT_OF_MEMORY: return "Out of memory";
case IERR_FILE_NOT_FOUND: return "File not found";
case IERR_FILE_IO: return "Disk I/O error";
case IERR_BAD_FORMAT: return "Bad archive";
case IERR_NAND_OPEN: return "NAND open error";
case IERR_NAND_READ: return "NAND read error";
case IERR_NAND_WRITE: return "NAND write error";
default: return "Unknown error!?";
}
}

View file

@ -0,0 +1,55 @@
/***************************************************************************
* __________ __ ___.
* 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_X1000_H__
#define __INSTALLER_X1000_H__
/* This API is for the bootloader recovery menu and Rockbox utility to handle
* bootloader installation, backup, and restore.
*
* Currently the installer can only handle NAND flash, although the X1000 can
* boot from NOR flash or SD/MMC. Support for other storage media can be added
* when there is a target that needs it.
*
* Bootloader updates are tarballs, and they can "monkey patch" the flash in
* a customizable way (but fixed at compile time).
*
* Backup and restore simply takes the range of eraseblocks touched by the
* monkey patch and copies them to or from a regular file.
*/
enum {
IERR_SUCCESS = 0,
IERR_OUT_OF_MEMORY,
IERR_FILE_NOT_FOUND,
IERR_FILE_IO,
IERR_BAD_FORMAT,
IERR_NAND_OPEN,
IERR_NAND_READ,
IERR_NAND_WRITE,
};
extern int install_bootloader(const char* filename);
extern int backup_bootloader(const char* filename);
extern int restore_bootloader(const char* filename);
extern const char* installer_strerror(int rc);
#endif /* __INSTALLER_X1000_H__ */