The uninitted RAM macro would fail because the OTA code would clear out the first 50KB or so of RAM to copy its code and global values. Move all global values to the end of RAM in OTA, and then align any uninitted vars to a 16KB unit to ensure it won't appear in the 1st part of RAM that is still needed to copy the OTA executable. In the normal case, without any uninit_ram vars, no additional space is used. When uninit_ram is used, up to 16KB of space may be lost to get that alignment, but it will work properly. Fixes #1188
198 lines
6.5 KiB
C
198 lines
6.5 KiB
C
/*
|
|
ota_lfs.c - LittleFS+GZIP support for OTA operations
|
|
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "ota_lfs.h"
|
|
#include "ota_command.h"
|
|
#include "hardware/sync.h"
|
|
#include "hardware/flash.h"
|
|
|
|
#include "../libraries/LittleFS/lib/littlefs/lfs.h"
|
|
#include "../libraries/LittleFS/lib/littlefs/lfs_util.h"
|
|
#include "./uzlib/src/uzlib.h"
|
|
|
|
OTACmdPage __attribute__((section (".globals"))) _ota_cmd;
|
|
|
|
static lfs_t _lfs;
|
|
static struct lfs_config _lfs_cfg;
|
|
|
|
static uint8_t *_start;
|
|
static uint32_t _blockSize;
|
|
static uint32_t _size;
|
|
|
|
// The actual flash accessing routines
|
|
static lfs_block_t _lastBlock;
|
|
static int lfs_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *dst, lfs_size_t size) {
|
|
(void) c;
|
|
memcpy(dst, _start + (block * _blockSize) + off, size);
|
|
_lastBlock = block;
|
|
return 0;
|
|
}
|
|
|
|
static int lfs_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
|
|
(void) c;
|
|
uint8_t *addr = _start + (block * _blockSize) + off;
|
|
int save = save_and_disable_interrupts();
|
|
flash_range_program((intptr_t)addr - (intptr_t)XIP_BASE, (const uint8_t *)buffer, size);
|
|
restore_interrupts(save);
|
|
return 0;
|
|
}
|
|
|
|
static int lfs_flash_erase(const struct lfs_config *c, lfs_block_t block) {
|
|
(void) c;
|
|
uint8_t *addr = _start + (block * _blockSize);
|
|
int save = save_and_disable_interrupts();
|
|
flash_range_erase((intptr_t)addr - (intptr_t)XIP_BASE, _blockSize);
|
|
restore_interrupts(save);
|
|
return 0;
|
|
}
|
|
|
|
void lfsEraseBlock(uint32_t blockToErase) {
|
|
lfs_flash_erase(&_lfs_cfg, blockToErase);
|
|
}
|
|
|
|
static int lfs_flash_sync(const struct lfs_config *c) {
|
|
/* NOOP */
|
|
(void) c;
|
|
return 0;
|
|
}
|
|
|
|
uint8_t __attribute__((section (".globals"))) _read_buffer[256];
|
|
uint8_t __attribute__((section (".globals"))) _prog_buffer[256];
|
|
uint8_t __attribute__((section (".globals"))) _lookahead_buffer[256];
|
|
bool lfsMount(uint8_t *start, uint32_t blockSize, uint32_t size) {
|
|
_start = start;
|
|
_blockSize = blockSize;
|
|
_size = size;
|
|
|
|
memset(&_lfs, 0, sizeof(_lfs));
|
|
memset(&_lfs_cfg, 0, sizeof(_lfs_cfg));
|
|
_lfs_cfg.context = NULL;
|
|
_lfs_cfg.read = lfs_flash_read;
|
|
_lfs_cfg.prog = lfs_flash_prog;
|
|
_lfs_cfg.erase = lfs_flash_erase;
|
|
_lfs_cfg.sync = lfs_flash_sync;
|
|
_lfs_cfg.read_size = 256;
|
|
_lfs_cfg.prog_size = 256;
|
|
_lfs_cfg.block_size = _blockSize;
|
|
_lfs_cfg.block_count = _blockSize ? _size / _blockSize : 0;
|
|
_lfs_cfg.block_cycles = 16; // TODO - need better explanation
|
|
_lfs_cfg.cache_size = 256;
|
|
_lfs_cfg.lookahead_size = 256;
|
|
_lfs_cfg.read_buffer = _read_buffer;
|
|
_lfs_cfg.prog_buffer = _prog_buffer;
|
|
_lfs_cfg.lookahead_buffer = _lookahead_buffer;
|
|
_lfs_cfg.name_max = 0;
|
|
_lfs_cfg.file_max = 0;
|
|
_lfs_cfg.attr_max = 0;
|
|
return lfs_mount(&_lfs, &_lfs_cfg) < 0 ? false : true;
|
|
}
|
|
|
|
static bool _gzip = false;
|
|
static lfs_file_t _file;
|
|
|
|
unsigned char __attribute__((section (".globals"))) uzlib_read_buff[4096];
|
|
unsigned char __attribute__((section (".globals"))) gzip_dict[32768];
|
|
uint8_t __attribute__((section (".globals")))_flash_buff[4096]; // no room for this on the stack
|
|
struct uzlib_uncomp __attribute__((section (".globals"))) m_uncomp;
|
|
|
|
uint8_t __attribute__((section (".globals")))_ota_buff[256];
|
|
struct lfs_file_config _ota_cfg = { (void *)_ota_buff, NULL, 0 };
|
|
|
|
uint8_t __attribute__((section (".globals")))_file_buff[256];
|
|
struct lfs_file_config _file_cfg = { (void *)_file_buff, NULL, 0 };
|
|
|
|
bool lfsReadOTA(OTACmdPage *ota, uint32_t *blockToErase) {
|
|
lfs_file_t f;
|
|
|
|
if (lfs_file_opencfg(&_lfs, &f, _OTA_COMMAND_FILE, LFS_O_RDONLY, &_ota_cfg) < 0) {
|
|
return false;
|
|
}
|
|
if (sizeof(*ota) != lfs_file_read(&_lfs, &f, ota, sizeof(*ota))) {
|
|
return false;
|
|
}
|
|
*blockToErase = _lastBlock;
|
|
// No need to close the file, it's open read-only and we're just going to reboot anyway
|
|
//lfs_file_close(&_lfs, &f);
|
|
return true;
|
|
}
|
|
|
|
static int uzlib_read_cb(struct uzlib_uncomp *m) {
|
|
m->source = uzlib_read_buff;
|
|
int len = lfs_file_read(&_lfs, &_file, uzlib_read_buff, sizeof(uzlib_read_buff));
|
|
m->source_limit = uzlib_read_buff + len;
|
|
return *(m->source++);
|
|
}
|
|
|
|
bool lfsOpen(const char *filename) {
|
|
_gzip = false;
|
|
if (lfs_file_opencfg(&_lfs, &_file, filename, LFS_O_RDONLY, &_file_cfg) < 0) {
|
|
return false;
|
|
}
|
|
char b[2];
|
|
if (sizeof(b) != lfs_file_read(&_lfs, &_file, b, sizeof(b))) {
|
|
return false;
|
|
}
|
|
lfs_file_rewind(&_lfs, &_file);
|
|
if ((b[0] == 0x1f) && (b[1] == 0x8b)) {
|
|
uzlib_init();
|
|
m_uncomp.source = NULL;
|
|
m_uncomp.source_limit = NULL;
|
|
m_uncomp.source_read_cb = uzlib_read_cb;
|
|
uzlib_uncompress_init(&m_uncomp, gzip_dict, sizeof(gzip_dict));
|
|
int res = uzlib_gzip_parse_header(&m_uncomp);
|
|
if (res != TINF_OK) {
|
|
lfs_file_rewind(&_lfs, &_file);
|
|
return false; // Error uncompress header read, could have been false alarm
|
|
}
|
|
_gzip = true;
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool lfsSeek(uint32_t offset) {
|
|
while (offset) {
|
|
uint32_t to_read = (offset > sizeof(_flash_buff)) ? sizeof(_flash_buff) : offset;
|
|
if (!lfsRead(to_read)) {
|
|
return false;
|
|
}
|
|
offset -= to_read;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint8_t *lfsRead(uint32_t len) {
|
|
if (!_gzip) {
|
|
int ret = lfs_file_read(&_lfs, &_file, _flash_buff, len);
|
|
return (len == ret) ? _flash_buff : NULL;
|
|
}
|
|
m_uncomp.dest_start = _flash_buff;
|
|
m_uncomp.dest = _flash_buff;
|
|
m_uncomp.dest_limit = _flash_buff + len;
|
|
int res = uzlib_uncompress(&m_uncomp);
|
|
if ((res != TINF_DONE) && (res != TINF_OK)) {
|
|
return NULL;
|
|
}
|
|
return _flash_buff;
|
|
}
|
|
|
|
void lfsClose() {
|
|
// No need to close the file, it's open read-only and we're just going to reboot anyway
|
|
//lfs_file_close(&_lfs, &_file);
|
|
}
|