From 06423cab58569ef01eb526e5f0d2f5c0c8917aa0 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Tue, 23 Nov 2021 20:13:52 +0000 Subject: x1000-installer: Initial commit of new framework This is a new flash installer framework for the X1000 targets. A bunch of this code is *UNTESTED* but there is an external test harness which allows the library to be built and tested on a PC. Once tests are written and the bugs are ironed out this framework will replace the existing installer code. New features: - Update tarballs are MD5-checksummed to guarantee integrity. - The flash map is no longer fixed -- updates are self describing and carry a map file which specifies the areas to update. - Can take full or partial backups with checksums computed on the fly. - Supports an additional verification mode which reads back data after writing to ensure the flash contents were not silently corrupted. Change-Id: I29a89190c7ff566019f6a844ad0571f01fb7192f --- lib/x1000-installer/include/xf_error.h | 43 ++++++++++ lib/x1000-installer/include/xf_flashmap.h | 91 +++++++++++++++++++++ lib/x1000-installer/include/xf_nandio.h | 130 ++++++++++++++++++++++++++++++ lib/x1000-installer/include/xf_package.h | 65 +++++++++++++++ lib/x1000-installer/include/xf_stream.h | 64 +++++++++++++++ lib/x1000-installer/include/xf_update.h | 53 ++++++++++++ 6 files changed, 446 insertions(+) create mode 100644 lib/x1000-installer/include/xf_error.h create mode 100644 lib/x1000-installer/include/xf_flashmap.h create mode 100644 lib/x1000-installer/include/xf_nandio.h create mode 100644 lib/x1000-installer/include/xf_package.h create mode 100644 lib/x1000-installer/include/xf_stream.h create mode 100644 lib/x1000-installer/include/xf_update.h (limited to 'lib/x1000-installer/include') diff --git a/lib/x1000-installer/include/xf_error.h b/lib/x1000-installer/include/xf_error.h new file mode 100644 index 0000000000..2f3f6a1a4f --- /dev/null +++ b/lib/x1000-installer/include/xf_error.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _XF_ERROR_H_ +#define _XF_ERROR_H_ + +enum { + XF_E_SUCCESS = 0, + XF_E_IO = -1, + XF_E_LINE_TOO_LONG = -2, + XF_E_FILENAME_TOO_LONG = -3, + XF_E_INT_OVERFLOW = -4, + XF_E_BUF_OVERFLOW = -5, + XF_E_SYNTAX_ERROR = -6, + XF_E_INVALID_PARAMETER = -7, + XF_E_NAND = -8, + XF_E_OUT_OF_MEMORY = -9, + XF_E_OUT_OF_RANGE = -10, + XF_E_VERIFY_FAILED = -11, + XF_E_CANNOT_OPEN_FILE = -12, +}; + +const char* xf_strerror(int err); + +#endif /* _XF_ERROR_H_ */ diff --git a/lib/x1000-installer/include/xf_flashmap.h b/lib/x1000-installer/include/xf_flashmap.h new file mode 100644 index 0000000000..b0470e58e0 --- /dev/null +++ b/lib/x1000-installer/include/xf_flashmap.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _XF_FLASHMAP_H_ +#define _XF_FLASHMAP_H_ + +#include "xf_stream.h" +#include + +#define XF_MAP_NAMELEN 63 + +enum { + XF_MAP_HAS_MD5 = 0x01, /* 'md5' field is valid */ + XF_MAP_HAS_FILE_LENGTH = 0x02, /* 'file_length' field is valid */ +}; + +struct xf_map { + char name[XF_MAP_NAMELEN+1]; /* file name */ + uint8_t md5[16]; /* MD5 sum of file */ + uint32_t file_length; /* length of file in bytes */ + uint32_t offset; /* offset in flash */ + uint32_t length; /* region length in flash, in bytes */ + int flags; +}; + +/* Parse a line with space- or tab-delimited fields of the form + * + * '-' + * + * - name can be up to XF_FMAP_NAMELEN characters long + * - md5 is 32 hexadecimal characters (case insensitive) + * - file_length, offset, and length are 32-bit unsigned integers + * and can be given in decimal or (with '0x' prefix) hexadecimal + * + * Parsed data is written to *map. Returns zero on success and + * nonzero on error. + */ +int xf_map_parseline(const char* line, struct xf_map* map); + +/* Parse a file calling xf_map_parseline() on each line to populate + * a map of up to 'maxnum' regions. Blank and comment lines are + * ignored (comments start with '#'). + * + * Returns the number of regions in the resulting map if the file was + * parsed successfully, or a negative value on error. + */ +int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum); + +/* Sort the map so its members are in ascending order with the lowest + * flash offset region first. After sorting, xf_map_validate() is used + * to check for overlapping regions. + * + * The return value is that of xf_map_validate(). + */ +int xf_map_sort(struct xf_map* map, int num); + +/* Check if the input map is sorted and contains no overlap. + * + * Returns 0 if the map is sorted and contains no overlapping regions, + * -1 if the map isn't sorted, or if an overlapping region is detected, + * the index of the first overlapping region. (A returned index i is + * always positive: the two overlapped entries are map[i] and map[i-1].) + */ +int xf_map_validate(const struct xf_map* map, int num); + +/* Write the map to a stream. This does not check that the map is valid. + * Returns the number of bytes written to the stream or a negative value + * on error. The stream may be NULL, in which case the number of bytes + * that would be written are returned (similar to snprintf). + */ +int xf_map_write(struct xf_map* map, int num, struct xf_stream* s); + +#endif /* _XF_FLASHMAP_H_ */ diff --git a/lib/x1000-installer/include/xf_nandio.h b/lib/x1000-installer/include/xf_nandio.h new file mode 100644 index 0000000000..a10b71992c --- /dev/null +++ b/lib/x1000-installer/include/xf_nandio.h @@ -0,0 +1,130 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _XF_NANDIO_H_ +#define _XF_NANDIO_H_ + +#include "nand-x1000.h" +#include +#include + +enum xf_nandio_mode { + XF_NANDIO_READ = 0, + XF_NANDIO_PROGRAM, + XF_NANDIO_VERIFY, +}; + +struct xf_nandio { + nand_drv* ndrv; + int nand_err; /* copy of the last NAND error code */ + int alloc_handle; + enum xf_nandio_mode mode; + + size_t block_size; /* size of a block, in bytes */ + size_t page_size; /* size of a page, in bytes */ + + uint8_t* old_buf; /* contains the 'old' block data already on chip */ + uint8_t* new_buf; /* contains the possibly modified 'new' block data */ + + nand_block_t cur_block; /* address of the current block */ + size_t offset_in_block; /* byte offset in the current block */ + unsigned block_valid: 1; /* 1 if buffered block data is valid */ +}; + +int xf_nandio_init(struct xf_nandio* nio); +void xf_nandio_destroy(struct xf_nandio* nio); + +/** Sets the operational mode, which determines read/write/flush semantics. + * + * - XF_NANDIO_READ: Accesses the chip in read-only mode. Writes are allowed, + * but should not be used. (Writes will modify a temporary buffer but this + * will not alter the flash contents.) + * + * - XF_NANDIO_PROGRAM: Writes are allowed to modify the flash contents. + * Writes within a block are accumulated in a temporary buffer. When + * crossing a block boundary, either by writing past the end the current + * block or by seeking to a new one, the data written to the temporary + * buffer is compared against the current flash contents. If the block + * has been modified, it is erased and any non-blank pages are programmed + * with the new data. + * + * - XF_NANDIO_VERIFY: This mode allows callers to easily check whether the + * flash contents match some expected contents. Callers "write" the expected + * contents as if programming it with XF_NANDIO_PROGRAM. When a block is + * flushed, if the written data doesn't match the block contents, an + * XF_E_VERIFY_FAILED error is returned. The flash contents will not be + * altered in this mode. + * + * \returns XF_E_SUCCESS or a negative error code on failure. + */ +int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode); + +/** Seek to a given byte offset in the NAND flash. + * + * If the new offset is outside the current block, the current block will + * be automatically flushed. Note this can result in verification or program + * failures as with any other flush. + * + * \returns XF_E_SUCCESS or a negative error code on failure. + */ +int xf_nandio_seek(struct xf_nandio* nio, size_t offset); + +/** Read or write a contiguous sequence of bytes from flash. + * + * The read or write starts at the current position and continues for `count` + * bytes. Both reads and writes may cross block boundaries. Modified blocks + * will be flushed automatically if the operation crosses a block boundary. + * + * After a successful read or write, the current position is advanced by + * exactly `count` bytes. After a failure, the position is indeterminate. + * + * \returns XF_E_SUCCESS or a negative error code on failure. + */ +int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count); +int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count); + +/** Get a pointer to the block buffer for direct read/write access. + * + * These functions can be used to read or write data without intermediate + * buffers. The caller passes in the amount of data to be transferred in + * `*count`. A pointer to part of the block buffer is returned in `*buf` + * and the number of bytes available in `*buf` is returned in `*count`. + * + * Data at the current position can be read from the returned buffer and + * it may be modified by writing to the buffer. The buffer is only valid + * until the next call to an `xf_nandio` function. + * + * The read/write position is advanced by the returned `*count` on success, + * and is unchanged on failure. + * + * \returns XF_E_SUCCESS or a negative error code on failure. + */ +int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count); + +/** Flush the buffered block to ensure all outstanding program or verification + * operations have been performed. This should only be called to ensure the + * final modified block is flushed after you have finished writing all data. + * + * \returns XF_E_SUCCESS or a negative error code on failure. + */ +int xf_nandio_flush(struct xf_nandio* nio); + +#endif /* _XF_NANDIO_H_ */ diff --git a/lib/x1000-installer/include/xf_package.h b/lib/x1000-installer/include/xf_package.h new file mode 100644 index 0000000000..6633766bd7 --- /dev/null +++ b/lib/x1000-installer/include/xf_package.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _XF_PACKAGE_H_ +#define _XF_PACKAGE_H_ + +/* package format + * + * - bootloader-info.txt (contains a version label and optional metadata) + * - flashmap.txt (describes the flash update map) + * - [package must contain any other files referenced by the flash map] + * - [other files may be present, but are ignored] + */ + +#include "xf_flashmap.h" +#include "xf_stream.h" +#include "microtar.h" + +struct xf_package { + int alloc_handle; + mtar_t* tar; + struct xf_map* map; + int map_size; + char* metadata; + size_t metadata_len; +}; + +/** Open an update package + * + * \param pkg Uninitialized package structure + * \param file Name of the package file + * \param dflt_map Default flash map for loading old format packages + * \param dflt_map_size Size of the default flash map + * \returns XF_E_SUCCESS or a negative error code + */ +int xf_package_open_ex(struct xf_package* pkg, const char* file, + const struct xf_map* dflt_map, int dflt_map_size); + +/** Close a package which was previously opened successfully */ +void xf_package_close(struct xf_package* pkg); + +static inline int xf_package_open(struct xf_package* pkg, const char* file) +{ + return xf_package_open_ex(pkg, file, NULL, 0); +} + +#endif /* _XF_PACKAGE_H_ */ diff --git a/lib/x1000-installer/include/xf_stream.h b/lib/x1000-installer/include/xf_stream.h new file mode 100644 index 0000000000..adbde1c6db --- /dev/null +++ b/lib/x1000-installer/include/xf_stream.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _XF_STREAM_H_ +#define _XF_STREAM_H_ + +#include +#include +#include /* ssize_t */ +#include "microtar.h" + +struct xf_stream; + +struct xf_stream_ops { + off_t(*xget_size)(struct xf_stream* s); + ssize_t(*xread)(struct xf_stream* s, void* buf, size_t len); + ssize_t(*xwrite)(struct xf_stream* s, const void* buf, size_t len); + int(*xclose)(struct xf_stream* s); +}; + +struct xf_stream { + intptr_t data; + const struct xf_stream_ops* ops; +}; + +inline size_t xf_stream_get_size(struct xf_stream* s) +{ return s->ops->xget_size(s); } + +inline ssize_t xf_stream_read(struct xf_stream* s, void* buf, size_t len) +{ return s->ops->xread(s, buf, len); } + +inline ssize_t xf_stream_write(struct xf_stream* s, const void* buf, size_t len) +{ return s->ops->xwrite(s, buf, len); } + +inline int xf_stream_close(struct xf_stream* s) +{ return s->ops->xclose(s); } + +int xf_open_file(const char* file, int flags, struct xf_stream* s); +int xf_open_tar(mtar_t* mtar, const char* file, struct xf_stream* s); +int xf_create_tar(mtar_t* mtar, const char* file, size_t size, struct xf_stream* s); + +/* Utility function needed for a few things */ +int xf_stream_read_lines(struct xf_stream* s, char* buf, size_t bufsz, + int(*callback)(int n, char* buf, void* arg), void* arg); + +#endif /* _XF_STREAM_H_ */ diff --git a/lib/x1000-installer/include/xf_update.h b/lib/x1000-installer/include/xf_update.h new file mode 100644 index 0000000000..e421a21793 --- /dev/null +++ b/lib/x1000-installer/include/xf_update.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * __________ __ ___. + * 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 _XF_UPDATE_H_ +#define _XF_UPDATE_H_ + +#include "xf_package.h" +#include "xf_nandio.h" +#include "xf_flashmap.h" + +typedef int(*xf_update_open_stream_cb)(void* arg, const char* file, + struct xf_stream* stream); + +enum xf_update_mode { + XF_UPDATE, + XF_BACKUP, + XF_VERIFY, +}; + +/** The main updater entry point + * + * \param mode Operational mode + * \param nio Initialized NAND I/O object. + * \param map Flash map describing what regions to update. + * \param map_size Number of entries in the map. + * \param open_stream Callback used to open a stream for each map entry. + * \param arg Argument passed to the `open_stream` callback. + * + * \returns XF_E_SUCCESS on success or a negative error code on failure. + */ +int xf_updater_run(enum xf_update_mode mode, struct xf_nandio* nio, + struct xf_map* map, int map_size, + xf_update_open_stream_cb open_stream, void* arg); + +#endif /* _XF_UPDATE_H_ */ -- cgit v1.2.3