diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-07-11 02:32:05 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-07-11 15:39:50 +0100 |
commit | 84362141a0d1e33b29d12162caba7537dd5684b8 (patch) | |
tree | d966b33e6049cc54abe04d9b66ef0db794ebe948 /firmware/target | |
parent | e9d228832ccbee7e486c45a51680c6f94ce1ab92 (diff) |
x1000: Unified flash bootloader installer
Change-Id: Ib1d41d4e7d663ff8a21eb08108c13568f7408533
Diffstat (limited to 'firmware/target')
4 files changed, 381 insertions, 304 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c deleted file mode 100644 index 8ce73bf09e..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c +++ /dev/null @@ -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; -} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h deleted file mode 100644 index eb700e6689..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h +++ /dev/null @@ -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__ */ diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.c b/firmware/target/mips/ingenic_x1000/installer-x1000.c new file mode 100644 index 0000000000..617e6645b7 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/installer-x1000.c @@ -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!?"; + } +} diff --git a/firmware/target/mips/ingenic_x1000/installer-x1000.h b/firmware/target/mips/ingenic_x1000/installer-x1000.h new file mode 100644 index 0000000000..b71839a907 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/installer-x1000.h @@ -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__ */ |