summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-11-23 20:13:52 +0000
committerAidan MacDonald <amachronic@protonmail.com>2021-11-27 15:28:19 -0500
commit06423cab58569ef01eb526e5f0d2f5c0c8917aa0 (patch)
treeb1a356600f6f218de8d8d1ad1e839aff65c96a0f
parent98f1271aec1fd461ab20a1ae145bba630a5750fb (diff)
downloadrockbox-06423cab58569ef01eb526e5f0d2f5c0c8917aa0.tar.gz
rockbox-06423cab58569ef01eb526e5f0d2f5c0c8917aa0.zip
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
-rw-r--r--lib/x1000-installer/.gitignore3
-rw-r--r--lib/x1000-installer/Makefile38
-rw-r--r--lib/x1000-installer/SOURCES6
-rw-r--r--lib/x1000-installer/include/xf_error.h43
-rw-r--r--lib/x1000-installer/include/xf_flashmap.h91
-rw-r--r--lib/x1000-installer/include/xf_nandio.h130
-rw-r--r--lib/x1000-installer/include/xf_package.h65
-rw-r--r--lib/x1000-installer/include/xf_stream.h64
-rw-r--r--lib/x1000-installer/include/xf_update.h53
-rw-r--r--lib/x1000-installer/src/xf_error.c42
-rw-r--r--lib/x1000-installer/src/xf_flashmap.c313
-rw-r--r--lib/x1000-installer/src/xf_nandio.c295
-rw-r--r--lib/x1000-installer/src/xf_package.c264
-rw-r--r--lib/x1000-installer/src/xf_stream.c214
-rw-r--r--lib/x1000-installer/src/xf_update.c149
-rw-r--r--lib/x1000-installer/test/main.c98
-rw-r--r--lib/x1000-installer/test_lib/core_alloc.c65
-rw-r--r--lib/x1000-installer/test_lib/core_alloc.h33
-rw-r--r--lib/x1000-installer/test_lib/fakenand.c270
-rw-r--r--lib/x1000-installer/test_lib/file.c11
-rw-r--r--lib/x1000-installer/test_lib/file.h18
-rw-r--r--lib/x1000-installer/test_lib/md5.c245
-rw-r--r--lib/x1000-installer/test_lib/md5.h18
-rw-r--r--lib/x1000-installer/test_lib/nand-x1000.h112
-rw-r--r--lib/x1000-installer/test_lib/pathfuncs.c130
-rw-r--r--lib/x1000-installer/test_lib/pathfuncs.h39
-rw-r--r--lib/x1000-installer/test_lib/strlcpy.c50
-rw-r--r--lib/x1000-installer/test_lib/strlcpy.h4
-rw-r--r--lib/x1000-installer/test_lib/system.h10
-rw-r--r--lib/x1000-installer/x1000-installer.make21
30 files changed, 2894 insertions, 0 deletions
diff --git a/lib/x1000-installer/.gitignore b/lib/x1000-installer/.gitignore
new file mode 100644
index 0000000000..dae360e116
--- /dev/null
+++ b/lib/x1000-installer/.gitignore
@@ -0,0 +1,3 @@
1xf_test
2fakeNAND.bin
3fakeNAND_meta.bin
diff --git a/lib/x1000-installer/Makefile b/lib/x1000-installer/Makefile
new file mode 100644
index 0000000000..35fa747aab
--- /dev/null
+++ b/lib/x1000-installer/Makefile
@@ -0,0 +1,38 @@
1OBJ = src/xf_error.o \
2 src/xf_flashmap.o \
3 src/xf_nandio.o \
4 src/xf_package.o \
5 src/xf_stream.o \
6 src/xf_update.o
7LIB = libx1000-installer.a
8
9TOBJ = test_lib/core_alloc.o \
10 test_lib/fakenand.o \
11 test_lib/file.o \
12 test_lib/pathfuncs.o \
13 test_lib/md5.o \
14 test_lib/strlcpy.o \
15 test/main.o
16TBIN = xf_test
17
18# dependency needs to be built manually
19MTARINC = -I../microtar/src
20MTARLIB = ../microtar/libmicrotar.a
21
22CPPFLAGS = -Iinclude -Itest_lib $(MTARINC) -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200809L
23CFLAGS = -std=c99 -Wall -Wextra -O2
24
25all: $(LIB) $(TBIN)
26
27%.o: %.c
28 $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
29
30$(LIB): $(OBJ)
31 $(AR) rcs $@ $^ >/dev/null
32
33$(TBIN): $(TOBJ) $(LIB) $(MTARLIB)
34 $(CC) -o $@ $^
35
36clean:
37 rm -f $(LIB) $(OBJ)
38 rm -f $(TBIN) $(TOBJ)
diff --git a/lib/x1000-installer/SOURCES b/lib/x1000-installer/SOURCES
new file mode 100644
index 0000000000..cdbd9148d8
--- /dev/null
+++ b/lib/x1000-installer/SOURCES
@@ -0,0 +1,6 @@
1src/xf_error.c
2src/xf_flashmap.c
3src/xf_nandio.c
4src/xf_package.c
5src/xf_stream.c
6src/xf_update.c
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _XF_ERROR_H_
23#define _XF_ERROR_H_
24
25enum {
26 XF_E_SUCCESS = 0,
27 XF_E_IO = -1,
28 XF_E_LINE_TOO_LONG = -2,
29 XF_E_FILENAME_TOO_LONG = -3,
30 XF_E_INT_OVERFLOW = -4,
31 XF_E_BUF_OVERFLOW = -5,
32 XF_E_SYNTAX_ERROR = -6,
33 XF_E_INVALID_PARAMETER = -7,
34 XF_E_NAND = -8,
35 XF_E_OUT_OF_MEMORY = -9,
36 XF_E_OUT_OF_RANGE = -10,
37 XF_E_VERIFY_FAILED = -11,
38 XF_E_CANNOT_OPEN_FILE = -12,
39};
40
41const char* xf_strerror(int err);
42
43#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _XF_FLASHMAP_H_
23#define _XF_FLASHMAP_H_
24
25#include "xf_stream.h"
26#include <stdint.h>
27
28#define XF_MAP_NAMELEN 63
29
30enum {
31 XF_MAP_HAS_MD5 = 0x01, /* 'md5' field is valid */
32 XF_MAP_HAS_FILE_LENGTH = 0x02, /* 'file_length' field is valid */
33};
34
35struct xf_map {
36 char name[XF_MAP_NAMELEN+1]; /* file name */
37 uint8_t md5[16]; /* MD5 sum of file */
38 uint32_t file_length; /* length of file in bytes */
39 uint32_t offset; /* offset in flash */
40 uint32_t length; /* region length in flash, in bytes */
41 int flags;
42};
43
44/* Parse a line with space- or tab-delimited fields of the form
45 * <name> <md5> <file_length> <offset> <length>
46 * <name> '-' <offset> <length>
47 *
48 * - name can be up to XF_FMAP_NAMELEN characters long
49 * - md5 is 32 hexadecimal characters (case insensitive)
50 * - file_length, offset, and length are 32-bit unsigned integers
51 * and can be given in decimal or (with '0x' prefix) hexadecimal
52 *
53 * Parsed data is written to *map. Returns zero on success and
54 * nonzero on error.
55 */
56int xf_map_parseline(const char* line, struct xf_map* map);
57
58/* Parse a file calling xf_map_parseline() on each line to populate
59 * a map of up to 'maxnum' regions. Blank and comment lines are
60 * ignored (comments start with '#').
61 *
62 * Returns the number of regions in the resulting map if the file was
63 * parsed successfully, or a negative value on error.
64 */
65int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum);
66
67/* Sort the map so its members are in ascending order with the lowest
68 * flash offset region first. After sorting, xf_map_validate() is used
69 * to check for overlapping regions.
70 *
71 * The return value is that of xf_map_validate().
72 */
73int xf_map_sort(struct xf_map* map, int num);
74
75/* Check if the input map is sorted and contains no overlap.
76 *
77 * Returns 0 if the map is sorted and contains no overlapping regions,
78 * -1 if the map isn't sorted, or if an overlapping region is detected,
79 * the index of the first overlapping region. (A returned index i is
80 * always positive: the two overlapped entries are map[i] and map[i-1].)
81 */
82int xf_map_validate(const struct xf_map* map, int num);
83
84/* Write the map to a stream. This does not check that the map is valid.
85 * Returns the number of bytes written to the stream or a negative value
86 * on error. The stream may be NULL, in which case the number of bytes
87 * that would be written are returned (similar to snprintf).
88 */
89int xf_map_write(struct xf_map* map, int num, struct xf_stream* s);
90
91#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _XF_NANDIO_H_
23#define _XF_NANDIO_H_
24
25#include "nand-x1000.h"
26#include <stdint.h>
27#include <stddef.h>
28
29enum xf_nandio_mode {
30 XF_NANDIO_READ = 0,
31 XF_NANDIO_PROGRAM,
32 XF_NANDIO_VERIFY,
33};
34
35struct xf_nandio {
36 nand_drv* ndrv;
37 int nand_err; /* copy of the last NAND error code */
38 int alloc_handle;
39 enum xf_nandio_mode mode;
40
41 size_t block_size; /* size of a block, in bytes */
42 size_t page_size; /* size of a page, in bytes */
43
44 uint8_t* old_buf; /* contains the 'old' block data already on chip */
45 uint8_t* new_buf; /* contains the possibly modified 'new' block data */
46
47 nand_block_t cur_block; /* address of the current block */
48 size_t offset_in_block; /* byte offset in the current block */
49 unsigned block_valid: 1; /* 1 if buffered block data is valid */
50};
51
52int xf_nandio_init(struct xf_nandio* nio);
53void xf_nandio_destroy(struct xf_nandio* nio);
54
55/** Sets the operational mode, which determines read/write/flush semantics.
56 *
57 * - XF_NANDIO_READ: Accesses the chip in read-only mode. Writes are allowed,
58 * but should not be used. (Writes will modify a temporary buffer but this
59 * will not alter the flash contents.)
60 *
61 * - XF_NANDIO_PROGRAM: Writes are allowed to modify the flash contents.
62 * Writes within a block are accumulated in a temporary buffer. When
63 * crossing a block boundary, either by writing past the end the current
64 * block or by seeking to a new one, the data written to the temporary
65 * buffer is compared against the current flash contents. If the block
66 * has been modified, it is erased and any non-blank pages are programmed
67 * with the new data.
68 *
69 * - XF_NANDIO_VERIFY: This mode allows callers to easily check whether the
70 * flash contents match some expected contents. Callers "write" the expected
71 * contents as if programming it with XF_NANDIO_PROGRAM. When a block is
72 * flushed, if the written data doesn't match the block contents, an
73 * XF_E_VERIFY_FAILED error is returned. The flash contents will not be
74 * altered in this mode.
75 *
76 * \returns XF_E_SUCCESS or a negative error code on failure.
77 */
78int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode);
79
80/** Seek to a given byte offset in the NAND flash.
81 *
82 * If the new offset is outside the current block, the current block will
83 * be automatically flushed. Note this can result in verification or program
84 * failures as with any other flush.
85 *
86 * \returns XF_E_SUCCESS or a negative error code on failure.
87 */
88int xf_nandio_seek(struct xf_nandio* nio, size_t offset);
89
90/** Read or write a contiguous sequence of bytes from flash.
91 *
92 * The read or write starts at the current position and continues for `count`
93 * bytes. Both reads and writes may cross block boundaries. Modified blocks
94 * will be flushed automatically if the operation crosses a block boundary.
95 *
96 * After a successful read or write, the current position is advanced by
97 * exactly `count` bytes. After a failure, the position is indeterminate.
98 *
99 * \returns XF_E_SUCCESS or a negative error code on failure.
100 */
101int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count);
102int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count);
103
104/** Get a pointer to the block buffer for direct read/write access.
105 *
106 * These functions can be used to read or write data without intermediate
107 * buffers. The caller passes in the amount of data to be transferred in
108 * `*count`. A pointer to part of the block buffer is returned in `*buf`
109 * and the number of bytes available in `*buf` is returned in `*count`.
110 *
111 * Data at the current position can be read from the returned buffer and
112 * it may be modified by writing to the buffer. The buffer is only valid
113 * until the next call to an `xf_nandio` function.
114 *
115 * The read/write position is advanced by the returned `*count` on success,
116 * and is unchanged on failure.
117 *
118 * \returns XF_E_SUCCESS or a negative error code on failure.
119 */
120int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count);
121
122/** Flush the buffered block to ensure all outstanding program or verification
123 * operations have been performed. This should only be called to ensure the
124 * final modified block is flushed after you have finished writing all data.
125 *
126 * \returns XF_E_SUCCESS or a negative error code on failure.
127 */
128int xf_nandio_flush(struct xf_nandio* nio);
129
130#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _XF_PACKAGE_H_
23#define _XF_PACKAGE_H_
24
25/* package format
26 *
27 * - bootloader-info.txt (contains a version label and optional metadata)
28 * - flashmap.txt (describes the flash update map)
29 * - [package must contain any other files referenced by the flash map]
30 * - [other files may be present, but are ignored]
31 */
32
33#include "xf_flashmap.h"
34#include "xf_stream.h"
35#include "microtar.h"
36
37struct xf_package {
38 int alloc_handle;
39 mtar_t* tar;
40 struct xf_map* map;
41 int map_size;
42 char* metadata;
43 size_t metadata_len;
44};
45
46/** Open an update package
47 *
48 * \param pkg Uninitialized package structure
49 * \param file Name of the package file
50 * \param dflt_map Default flash map for loading old format packages
51 * \param dflt_map_size Size of the default flash map
52 * \returns XF_E_SUCCESS or a negative error code
53 */
54int xf_package_open_ex(struct xf_package* pkg, const char* file,
55 const struct xf_map* dflt_map, int dflt_map_size);
56
57/** Close a package which was previously opened successfully */
58void xf_package_close(struct xf_package* pkg);
59
60static inline int xf_package_open(struct xf_package* pkg, const char* file)
61{
62 return xf_package_open_ex(pkg, file, NULL, 0);
63}
64
65#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _XF_STREAM_H_
23#define _XF_STREAM_H_
24
25#include <stddef.h>
26#include <stdint.h>
27#include <sys/types.h> /* ssize_t */
28#include "microtar.h"
29
30struct xf_stream;
31
32struct xf_stream_ops {
33 off_t(*xget_size)(struct xf_stream* s);
34 ssize_t(*xread)(struct xf_stream* s, void* buf, size_t len);
35 ssize_t(*xwrite)(struct xf_stream* s, const void* buf, size_t len);
36 int(*xclose)(struct xf_stream* s);
37};
38
39struct xf_stream {
40 intptr_t data;
41 const struct xf_stream_ops* ops;
42};
43
44inline size_t xf_stream_get_size(struct xf_stream* s)
45{ return s->ops->xget_size(s); }
46
47inline ssize_t xf_stream_read(struct xf_stream* s, void* buf, size_t len)
48{ return s->ops->xread(s, buf, len); }
49
50inline ssize_t xf_stream_write(struct xf_stream* s, const void* buf, size_t len)
51{ return s->ops->xwrite(s, buf, len); }
52
53inline int xf_stream_close(struct xf_stream* s)
54{ return s->ops->xclose(s); }
55
56int xf_open_file(const char* file, int flags, struct xf_stream* s);
57int xf_open_tar(mtar_t* mtar, const char* file, struct xf_stream* s);
58int xf_create_tar(mtar_t* mtar, const char* file, size_t size, struct xf_stream* s);
59
60/* Utility function needed for a few things */
61int xf_stream_read_lines(struct xf_stream* s, char* buf, size_t bufsz,
62 int(*callback)(int n, char* buf, void* arg), void* arg);
63
64#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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _XF_UPDATE_H_
23#define _XF_UPDATE_H_
24
25#include "xf_package.h"
26#include "xf_nandio.h"
27#include "xf_flashmap.h"
28
29typedef int(*xf_update_open_stream_cb)(void* arg, const char* file,
30 struct xf_stream* stream);
31
32enum xf_update_mode {
33 XF_UPDATE,
34 XF_BACKUP,
35 XF_VERIFY,
36};
37
38/** The main updater entry point
39 *
40 * \param mode Operational mode
41 * \param nio Initialized NAND I/O object.
42 * \param map Flash map describing what regions to update.
43 * \param map_size Number of entries in the map.
44 * \param open_stream Callback used to open a stream for each map entry.
45 * \param arg Argument passed to the `open_stream` callback.
46 *
47 * \returns XF_E_SUCCESS on success or a negative error code on failure.
48 */
49int xf_updater_run(enum xf_update_mode mode, struct xf_nandio* nio,
50 struct xf_map* map, int map_size,
51 xf_update_open_stream_cb open_stream, void* arg);
52
53#endif /* _XF_UPDATE_H_ */
diff --git a/lib/x1000-installer/src/xf_error.c b/lib/x1000-installer/src/xf_error.c
new file mode 100644
index 0000000000..3d4b342a92
--- /dev/null
+++ b/lib/x1000-installer/src/xf_error.c
@@ -0,0 +1,42 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_error.h"
23
24const char* xf_strerror(int err)
25{
26 switch(err) {
27 case XF_E_SUCCESS: return "Success";
28 case XF_E_IO: return "I/O error";
29 case XF_E_LINE_TOO_LONG: return "Line too long";
30 case XF_E_FILENAME_TOO_LONG: return "Filename too long";
31 case XF_E_INT_OVERFLOW: return "Numeric overflow";
32 case XF_E_BUF_OVERFLOW: return "Buffer overflowed";
33 case XF_E_SYNTAX_ERROR: return "Syntax error";
34 case XF_E_INVALID_PARAMETER: return "Invalid parameter";
35 case XF_E_NAND: return "NAND flash error";
36 case XF_E_OUT_OF_MEMORY: return "Out of memory";
37 case XF_E_OUT_OF_RANGE: return "Out of range";
38 case XF_E_VERIFY_FAILED: return "Verification failed";
39 case XF_E_CANNOT_OPEN_FILE: return "Cannot open file";
40 default: return "Unknown error";
41 }
42}
diff --git a/lib/x1000-installer/src/xf_flashmap.c b/lib/x1000-installer/src/xf_flashmap.c
new file mode 100644
index 0000000000..75cd3c5905
--- /dev/null
+++ b/lib/x1000-installer/src/xf_flashmap.c
@@ -0,0 +1,313 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_flashmap.h"
23#include "xf_error.h"
24#include <stdbool.h>
25#include <stdlib.h>
26#include <ctype.h>
27#include <string.h>
28
29static int xdigit_to_int(char c)
30{
31 if(c >= 'a' && c <= 'f')
32 return 10 + (c - 'a');
33 if(c >= 'A' && c <= 'F')
34 return 10 + (c - 'A');
35 if(c >= '0' && c <= '9')
36 return c - '0';
37 return -1;
38}
39
40int xf_map_parseline(const char* line, struct xf_map* map)
41{
42 enum {
43 s_name,
44 s_md5,
45 s_first_num,
46 s_file_len = s_first_num,
47 s_offset,
48 s_length,
49 s_done,
50 };
51
52#define skipws() do { while(*line == ' ' || *line == '\t') ++line; } while(0)
53#define nextstate() do { ++state; length = 0; ++line; skipws(); } while(0)
54
55 int state = s_name;
56 int length = 0;
57 int digit_val;
58 uint32_t int_val;
59 uint32_t* num_ptr[3] = {&map->file_length, &map->offset, &map->length};
60 bool has_md5 = true;
61
62 skipws();
63 while(*line && *line != '\n') {
64 switch(state) {
65 case s_name:
66 if(*line == ' ' || *line == '\t') {
67 nextstate();
68 continue;
69 } else if(isgraph((unsigned char)*line)) {
70 if(length == XF_MAP_NAMELEN)
71 return XF_E_FILENAME_TOO_LONG;
72
73 map->name[length++] = *line++;
74 map->name[length] = '\0';
75 continue;
76 } else {
77 return XF_E_SYNTAX_ERROR;
78 }
79
80 case s_md5:
81 if(*line == '-') {
82 memset(map->md5, 0, 16);
83 map->file_length = 0;
84 has_md5 = false;
85 ++line;
86 } else {
87 for(int i = 0; i < 16; ++i) {
88 int_val = 0;
89 for(int j = 0; j < 2; ++j) {
90 digit_val = xdigit_to_int(*line++);
91 if(digit_val < 0)
92 return XF_E_SYNTAX_ERROR;
93
94 int_val <<= 4;
95 int_val |= digit_val;
96 }
97
98 map->md5[i] = int_val;
99 }
100 }
101
102 if(*line == ' ' || *line == '\t') {
103 /* skip file length if md5 is not present */
104 if(!has_md5)
105 ++state;
106
107 nextstate();
108 continue;
109 } else {
110 return XF_E_SYNTAX_ERROR;
111 }
112
113 case s_file_len:
114 case s_offset:
115 case s_length:
116 int_val = 0;
117
118 if(*line == '0') {
119 ++line;
120 if(*line == 'x' || *line == 'X') {
121 ++line;
122 while((digit_val = xdigit_to_int(*line)) >= 0) {
123 ++line;
124
125 if(int_val > UINT32_MAX/16)
126 return XF_E_INT_OVERFLOW;
127 int_val *= 16;
128
129 if(int_val > UINT32_MAX - digit_val)
130 return XF_E_INT_OVERFLOW;
131 int_val |= digit_val;
132 }
133 }
134 } else if(*line >= '1' && *line <= '9') {
135 do {
136 if(int_val > UINT32_MAX/10)
137 return XF_E_INT_OVERFLOW;
138 int_val *= 10;
139
140 digit_val = *line++ - '0';
141 if(int_val > UINT32_MAX - digit_val)
142 return XF_E_INT_OVERFLOW;
143
144 int_val += digit_val;
145 } while(*line >= '0' && *line <= '9');
146 }
147
148 *num_ptr[state - s_first_num] = int_val;
149
150 if(*line == ' ' || *line == '\t') {
151 nextstate();
152 continue;
153 } else if(state+1 == s_done && *line == '\0') {
154 /* end of input */
155 continue;
156 } else {
157 return XF_E_SYNTAX_ERROR;
158 }
159
160 case s_done:
161 if(isspace(*line)) {
162 line++;
163 continue; /* swallow trailing spaces, carriage return, etc */
164 } else
165 return XF_E_SYNTAX_ERROR;
166 }
167 }
168
169#undef skipws
170#undef nextstate
171
172 /* one last overflow check - ensure mapped range is addressable */
173 if(map->offset > UINT32_MAX - map->length)
174 return XF_E_INT_OVERFLOW;
175
176 if(has_md5)
177 map->flags = XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
178 else
179 map->flags = 0;
180
181 return XF_E_SUCCESS;
182}
183
184struct map_parse_args {
185 struct xf_map* map;
186 int num;
187 int maxnum;
188};
189
190int map_parse_line_cb(int n, char* buf, void* arg)
191{
192 (void)n;
193
194 struct map_parse_args* args = arg;
195
196 /* ignore comments and blank lines */
197 if(*buf == '#' || *buf == '\0')
198 return 0;
199
200 struct xf_map dummy_map;
201 struct xf_map* dst_map;
202 if(args->num < args->maxnum)
203 dst_map = &args->map[args->num];
204 else
205 dst_map = &dummy_map;
206
207 int rc = xf_map_parseline(buf, dst_map);
208 if(rc)
209 return rc;
210
211 args->num++;
212 return 0;
213}
214
215int xf_map_parse(struct xf_stream* s, struct xf_map* map, int maxnum)
216{
217 char buf[200];
218 struct map_parse_args args;
219 args.map = map;
220 args.num = 0;
221 args.maxnum = maxnum;
222
223 int rc = xf_stream_read_lines(s, buf, sizeof(buf),
224 map_parse_line_cb, &args);
225 if(rc < 0)
226 return rc;
227
228 return args.num;
229}
230
231static int xf_map_compare(const void* a, const void* b)
232{
233 const struct xf_map* mapA = a;
234 const struct xf_map* mapB = b;
235
236 if(mapA->offset < mapB->offset)
237 return -1;
238 else if(mapA->offset == mapB->offset)
239 return 0;
240 else
241 return 1;
242}
243
244int xf_map_sort(struct xf_map* map, int num)
245{
246 qsort(map, num, sizeof(struct xf_map), xf_map_compare);
247 return xf_map_validate(map, num);
248}
249
250int xf_map_validate(const struct xf_map* map, int num)
251{
252 for(int i = 1; i < num; ++i)
253 if(map[i].offset <= map[i-1].offset)
254 return -1;
255
256 for(int i = 1; i < num; ++i)
257 if(map[i-1].offset + map[i-1].length > map[i].offset)
258 return i;
259
260 return 0;
261}
262
263int xf_map_write(struct xf_map* map, int num, struct xf_stream* s)
264{
265 static const char hex[] = "0123456789abcdef";
266 char buf[200];
267 char md5str[33];
268 int total_len = 0;
269
270 md5str[32] = '\0';
271
272 for(int i = 0; i < num; ++i) {
273 bool has_md5 = false;
274 if(map->flags & XF_MAP_HAS_MD5) {
275 if(!(map->flags & XF_MAP_HAS_FILE_LENGTH))
276 return XF_E_INVALID_PARAMETER;
277
278 has_md5 = true;
279 for(int j = 0; j < 16; ++j) {
280 uint8_t byte = map[i].md5[j];
281 md5str[2*j] = hex[(byte >> 4) & 0xf];
282 md5str[2*j+1] = hex[byte & 0xf];
283 }
284 }
285
286 int len;
287 if(!has_md5) {
288 len = snprintf(buf, sizeof(buf), "%s - %lx %lu\n",
289 map[i].name,
290 (unsigned long)map[i].offset,
291 (unsigned long)map[i].length);
292 } else {
293 len = snprintf(buf, sizeof(buf), "%s %s %lu 0x%lx %lu\n",
294 map[i].name, md5str,
295 (unsigned long)map[i].file_length,
296 (unsigned long)map[i].offset,
297 (unsigned long)map[i].length);
298 }
299
300 if(len < 0 || (size_t)len >= sizeof(buf))
301 return XF_E_LINE_TOO_LONG;
302
303 if(s) {
304 int rc = xf_stream_write(s, buf, len);
305 if(rc != len)
306 return XF_E_IO;
307 }
308
309 total_len += len;
310 }
311
312 return total_len;
313}
diff --git a/lib/x1000-installer/src/xf_nandio.c b/lib/x1000-installer/src/xf_nandio.c
new file mode 100644
index 0000000000..ba79cbbcbf
--- /dev/null
+++ b/lib/x1000-installer/src/xf_nandio.c
@@ -0,0 +1,295 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_nandio.h"
23#include "xf_error.h"
24#include "core_alloc.h"
25#include "system.h"
26#include <string.h>
27#include <stdbool.h>
28
29int xf_nandio_init(struct xf_nandio* nio)
30{
31 int rc;
32
33 memset(nio, 0, sizeof(*nio));
34
35 /* open NAND */
36 nio->ndrv = nand_init();
37 nand_lock(nio->ndrv);
38 rc = nand_open(nio->ndrv);
39 if(rc != NAND_SUCCESS) {
40 nio->nand_err = rc;
41 rc = XF_E_NAND;
42 goto out;
43 }
44
45 /* read chip parameters */
46 nio->page_size = nio->ndrv->chip->page_size;
47 nio->block_size = nio->page_size << nio->ndrv->chip->log2_ppb;
48
49 /* allocate memory */
50 size_t alloc_size = 0;
51 alloc_size += CACHEALIGN_SIZE - 1;
52 alloc_size += nio->block_size * 2;
53
54 nio->alloc_handle = core_alloc("xf_nandio", alloc_size);
55 if(nio->alloc_handle < 0) {
56 rc = XF_E_OUT_OF_MEMORY;
57 goto out_nclose;
58 }
59
60 uint8_t* buffer = core_get_data(nio->alloc_handle);
61 CACHEALIGN_BUFFER(buffer, alloc_size);
62
63 nio->old_buf = buffer;
64 nio->new_buf = &buffer[nio->block_size];
65
66 rc = XF_E_SUCCESS;
67 goto out;
68
69 out_nclose:
70 nand_close(nio->ndrv);
71 out:
72 nand_unlock(nio->ndrv);
73 return rc;
74}
75
76void xf_nandio_destroy(struct xf_nandio* nio)
77{
78 if(nio->alloc_handle > 0) {
79 core_free(nio->alloc_handle);
80 nio->alloc_handle = 0;
81 }
82
83 if(nio->ndrv) {
84 nand_lock(nio->ndrv);
85 nand_close(nio->ndrv);
86 nand_unlock(nio->ndrv);
87 nio->ndrv = NULL;
88 }
89}
90
91static bool is_page_blank(const uint8_t* buf, uint32_t length)
92{
93 for(uint32_t i = 0; i < length; ++i)
94 if(buf[i] != 0xff)
95 return false;
96
97 return true;
98}
99
100static int flush_block(struct xf_nandio* nio, bool invalidate)
101{
102 /* no block, or only reading - flush is a no-op */
103 if(!nio->block_valid || nio->mode == XF_NANDIO_READ)
104 return XF_E_SUCCESS;
105
106 /* nothing to do if new data is same as old data */
107 if(!memcmp(nio->old_buf, nio->new_buf, nio->block_size))
108 return XF_E_SUCCESS;
109
110 /* data mismatch during verification - report the error */
111 if(nio->mode == XF_NANDIO_VERIFY)
112 return XF_E_VERIFY_FAILED;
113
114 /* erase the block */
115 int rc = nand_block_erase(nio->ndrv, nio->cur_block);
116 if(rc != NAND_SUCCESS) {
117 nio->block_valid = false;
118 nio->nand_err = rc;
119 return XF_E_NAND;
120 }
121
122 size_t oob_size = nio->ndrv->chip->oob_size;
123
124 unsigned page = 0;
125 nand_page_t page_addr = nio->cur_block;
126 for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
127 /* skip programming blank pages to go faster & reduce wear */
128 uint8_t* page_data = &nio->new_buf[page * nio->page_size];
129 if(is_page_blank(page_data, nio->page_size))
130 continue;
131
132 /* copy page and write blank OOB data */
133 memcpy(nio->ndrv->page_buf, page_data, nio->page_size);
134 memset(&nio->ndrv->page_buf[nio->page_size], 0xff, oob_size);
135
136 /* program the page */
137 rc = nand_page_program(nio->ndrv, page_addr, nio->ndrv->page_buf);
138 if(rc != NAND_SUCCESS) {
139 nio->block_valid = false;
140 nio->nand_err = rc;
141 return XF_E_NAND;
142 }
143 }
144
145 if(invalidate)
146 nio->block_valid = false;
147 else {
148 /* update our 'old' buffer so a subsequent flush
149 * will not reprogram the same block */
150 memcpy(nio->old_buf, nio->new_buf, nio->block_size);
151 }
152
153 return XF_E_SUCCESS;
154}
155
156static int seek_to_block(struct xf_nandio* nio, nand_block_t block_addr,
157 size_t offset_in_block)
158{
159 /* already on this block? */
160 if(nio->block_valid && block_addr == nio->cur_block) {
161 nio->offset_in_block = offset_in_block;
162 return XF_E_SUCCESS;
163 }
164
165 /* ensure new block is within range */
166 if(block_addr >= (nio->ndrv->chip->nr_blocks << nio->ndrv->chip->log2_ppb))
167 return XF_E_OUT_OF_RANGE;
168
169 /* flush old block */
170 int rc = flush_block(nio, true);
171 if(rc)
172 return rc;
173
174 nio->block_valid = false;
175
176 /* read the new block */
177 unsigned page = 0;
178 nand_page_t page_addr = block_addr;
179 for(; page < nio->ndrv->ppb; ++page, ++page_addr) {
180 rc = nand_page_read(nio->ndrv, page_addr, nio->ndrv->page_buf);
181 if(rc != NAND_SUCCESS) {
182 nio->nand_err = rc;
183 return XF_E_NAND;
184 }
185
186 memcpy(&nio->old_buf[page * nio->page_size], nio->ndrv->page_buf, nio->page_size);
187 }
188
189 /* copy to 2nd buffer */
190 memcpy(nio->new_buf, nio->old_buf, nio->block_size);
191
192 /* update position */
193 nio->cur_block = block_addr;
194 nio->offset_in_block = offset_in_block;
195 nio->block_valid = true;
196 return XF_E_SUCCESS;
197}
198
199int xf_nandio_set_mode(struct xf_nandio* nio, enum xf_nandio_mode mode)
200{
201 nand_lock(nio->ndrv);
202
203 /* flush the current block before switching to the new mode,
204 * to ensure consistency */
205 int rc = flush_block(nio, false);
206 if(rc)
207 goto err;
208
209 nio->mode = mode;
210 rc = XF_E_SUCCESS;
211
212 err:
213 nand_unlock(nio->ndrv);
214 return rc;
215}
216
217static int nandio_rdwr(struct xf_nandio* nio, void* buf, size_t count, bool write)
218{
219 while(count > 0) {
220 void* ptr;
221 size_t amount = count;
222 int rc = xf_nandio_get_buffer(nio, &ptr, &amount);
223 if(rc)
224 return rc;
225
226 if(write)
227 memcpy(ptr, buf, amount);
228 else
229 memcpy(buf, ptr, amount);
230
231 count -= amount;
232 }
233
234 return XF_E_SUCCESS;
235}
236
237int xf_nandio_seek(struct xf_nandio* nio, size_t offset)
238{
239 uint32_t block_nr = offset / nio->block_size;
240 size_t offset_in_block = offset % nio->block_size;
241 nand_block_t block_addr = block_nr << nio->ndrv->chip->log2_ppb;
242
243 nand_lock(nio->ndrv);
244 int rc = seek_to_block(nio, block_addr, offset_in_block);
245 nand_unlock(nio->ndrv);
246
247 return rc;
248}
249
250int xf_nandio_read(struct xf_nandio* nio, void* buf, size_t count)
251{
252 return nandio_rdwr(nio, buf, count, false);
253}
254
255int xf_nandio_write(struct xf_nandio* nio, const void* buf, size_t count)
256{
257 return nandio_rdwr(nio, (void*)buf, count, true);
258}
259
260int xf_nandio_get_buffer(struct xf_nandio* nio, void** buf, size_t* count)
261{
262 nand_lock(nio->ndrv);
263
264 /* make sure the current block data is read in */
265 int rc = seek_to_block(nio, nio->cur_block, nio->offset_in_block);
266 if(rc)
267 goto err;
268
269 size_t amount_left = nio->block_size - nio->offset_in_block;
270 if(amount_left == 0) {
271 amount_left = nio->block_size;
272 rc = seek_to_block(nio, nio->cur_block + nio->ndrv->ppb, 0);
273 if(rc)
274 goto err;
275 }
276
277 *buf = &nio->new_buf[nio->offset_in_block];
278 *count = MIN(*count, amount_left);
279
280 nio->offset_in_block += *count;
281 rc = XF_E_SUCCESS;
282
283 err:
284 nand_unlock(nio->ndrv);
285 return rc;
286}
287
288int xf_nandio_flush(struct xf_nandio* nio)
289{
290 nand_lock(nio->ndrv);
291 int rc = flush_block(nio, false);
292 nand_unlock(nio->ndrv);
293
294 return rc;
295}
diff --git a/lib/x1000-installer/src/xf_package.c b/lib/x1000-installer/src/xf_package.c
new file mode 100644
index 0000000000..78bddded68
--- /dev/null
+++ b/lib/x1000-installer/src/xf_package.c
@@ -0,0 +1,264 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_package.h"
23#include "xf_error.h"
24#include "pathfuncs.h"
25#include "file.h"
26#include "core_alloc.h"
27#include "md5.h"
28#include "system.h"
29#include <stdbool.h>
30#include <string.h>
31
32#ifdef ROCKBOX
33# include "microtar-rockbox.h"
34#else
35# include "microtar-stdio.h"
36#endif
37
38#define METADATA_SIZE 4096 /* size of the metadata buffer */
39#define MAX_MAP_SIZE 32 /* maximum number of map entries */
40
41static int pkg_alloc(struct xf_package* pkg)
42{
43 memset(pkg, 0, sizeof(*pkg));
44
45 /* calculate allocation size */
46 size_t alloc_size = 0;
47 alloc_size += ALIGN_UP_P2(sizeof(mtar_t), 3);
48 alloc_size += ALIGN_UP_P2(sizeof(struct xf_map) * MAX_MAP_SIZE, 3);
49 alloc_size += ALIGN_UP_P2(METADATA_SIZE, 3);
50 alloc_size += 7; /* for alignment */
51
52 pkg->alloc_handle = core_alloc("xf_package", alloc_size);
53 if(pkg->alloc_handle < 0)
54 return XF_E_OUT_OF_MEMORY;
55
56 /* distribute memory */
57 uint8_t* buf = (uint8_t*)core_get_data(pkg->alloc_handle);
58 memset(buf, 0, alloc_size);
59 ALIGN_BUFFER(buf, alloc_size, 8);
60
61 pkg->tar = (mtar_t*)buf;
62 buf += ALIGN_UP_P2(sizeof(mtar_t), 3);
63
64 pkg->map = (struct xf_map*)buf;
65 buf += ALIGN_UP_P2(sizeof(struct xf_map) * MAX_MAP_SIZE, 3);
66
67 pkg->metadata = (char*)buf;
68 buf += ALIGN_UP_P2(METADATA_SIZE, 3);
69
70 return XF_E_SUCCESS;
71}
72
73static int read_meta_line_cb(int n, char* buf, void* arg)
74{
75 struct xf_package* pkg = (struct xf_package*)arg;
76 size_t length = strlen(buf);
77
78 /* skip blank lines and the first line (it's reserved for old format) */
79 if(n == 0 || length == 0)
80 return 0;
81
82 /* metadata lines require an '=' sign to separate key and value */
83 if(!strchr(buf, '='))
84 return XF_E_SYNTAX_ERROR;
85
86 /* we need space to copy the key-value pair plus a null terminator */
87 if(length + 1 >= METADATA_SIZE - pkg->metadata_len)
88 return XF_E_BUF_OVERFLOW;
89
90 memcpy(&pkg->metadata[pkg->metadata_len], buf, length + 1);
91 pkg->metadata_len += length + 1;
92 return 0;
93}
94
95static int pkg_read_meta(struct xf_package* pkg)
96{
97 struct xf_stream stream;
98 int rc = xf_open_tar(pkg->tar, "bootloader-info.txt", &stream);
99 if(rc)
100 return XF_E_CANNOT_OPEN_FILE;
101
102 char buf[200];
103 rc = xf_stream_read_lines(&stream, buf, sizeof(buf), read_meta_line_cb, pkg);
104 xf_stream_close(&stream);
105 return rc;
106}
107
108static int pkg_read_map(struct xf_package* pkg,
109 const struct xf_map* dflt_map, int dflt_map_size)
110{
111 /* Attempt to load and parse the map file */
112 struct xf_stream stream;
113 int rc = xf_open_tar(pkg->tar, "flashmap.txt", &stream);
114
115 /* If the flash map is absent but a default map has been provided,
116 * then the update is in the old fixed format. */
117 if(rc == MTAR_ENOTFOUND && dflt_map) {
118 if(dflt_map_size > MAX_MAP_SIZE)
119 return XF_E_INVALID_PARAMETER;
120
121 for(int i = 0; i < dflt_map_size; ++i) {
122 pkg->map[i] = dflt_map[i];
123 pkg->map[i].flags &= ~(XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH);
124 }
125
126 pkg->map_size = dflt_map_size;
127 return XF_E_SUCCESS;
128 }
129
130 if(rc != MTAR_ESUCCESS)
131 return XF_E_CANNOT_OPEN_FILE;
132
133 rc = xf_map_parse(&stream, pkg->map, MAX_MAP_SIZE);
134 if(rc < 0)
135 goto err;
136
137 /* Sort the map; reject it if there is any overlap. */
138 pkg->map_size = rc;
139 if(xf_map_sort(pkg->map, pkg->map_size)) {
140 rc = XF_E_INVALID_PARAMETER;
141 goto err;
142 }
143
144 /* All packages in the 'new' format are required to have MD5 sums. */
145 for(int i = 0; i < pkg->map_size; ++i) {
146 if(!(pkg->map[i].flags & XF_MAP_HAS_MD5) ||
147 !(pkg->map[i].flags & XF_MAP_HAS_FILE_LENGTH)) {
148 rc = XF_E_VERIFY_FAILED;
149 goto err;
150 }
151 }
152
153 rc = XF_E_SUCCESS;
154
155 err:
156 xf_stream_close(&stream);
157 return rc;
158}
159
160static int pkg_verify(struct xf_package* pkg)
161{
162 struct xf_stream stream;
163 md5_context ctx;
164 uint8_t buffer[128];
165
166 for(int i = 0; i < pkg->map_size; ++i) {
167 /* At a bare minimum, check that the file exists. */
168 int rc = xf_open_tar(pkg->tar, pkg->map[i].name, &stream);
169 if(rc)
170 return XF_E_VERIFY_FAILED;
171
172 /* Also check that it isn't bigger than the update region.
173 * That would normally indicate a problem. */
174 off_t streamsize = xf_stream_get_size(&stream);
175 if(streamsize > (off_t)pkg->map[i].length) {
176 rc = XF_E_VERIFY_FAILED;
177 goto err;
178 }
179
180 /* Check against the listed file length. */
181 if(pkg->map[i].flags & XF_MAP_HAS_FILE_LENGTH) {
182 if(streamsize != (off_t)pkg->map[i].file_length) {
183 rc = XF_E_VERIFY_FAILED;
184 goto err;
185 }
186 }
187
188 /* Check the MD5 sum if we have it. */
189 if(pkg->map[i].flags & XF_MAP_HAS_MD5) {
190 md5_starts(&ctx);
191 while(1) {
192 ssize_t n = xf_stream_read(&stream, buffer, sizeof(buffer));
193 if(n < 0) {
194 rc = XF_E_IO;
195 goto err;
196 }
197
198 md5_update(&ctx, buffer, n);
199 if((size_t)n < sizeof(buffer))
200 break;
201 }
202
203 md5_finish(&ctx, buffer);
204 if(memcpy(buffer, pkg->map[i].md5, 16)) {
205 rc = XF_E_VERIFY_FAILED;
206 goto err;
207 }
208 }
209
210 err:
211 xf_stream_close(&stream);
212 if(rc)
213 return rc;
214 }
215
216 /* All files passed verification */
217 return XF_E_SUCCESS;
218}
219
220int xf_package_open_ex(struct xf_package* pkg, const char* file,
221 const struct xf_map* dflt_map, int dflt_map_size)
222{
223 int rc = pkg_alloc(pkg);
224 if(rc)
225 return rc;
226
227#ifdef ROCKBOX
228 rc = mtar_open(pkg->tar, file, O_RDONLY);
229#else
230 rc = mtar_open(pkg->tar, file, "r");
231#endif
232 if(rc != MTAR_ESUCCESS) {
233 rc = XF_E_CANNOT_OPEN_FILE;
234 goto err;
235 }
236
237 rc = pkg_read_meta(pkg);
238 if(rc)
239 goto err;
240
241 rc = pkg_read_map(pkg, dflt_map, dflt_map_size);
242 if(rc)
243 goto err;
244
245 rc = pkg_verify(pkg);
246 if(rc)
247 goto err;
248
249 err:
250 if(rc)
251 xf_package_close(pkg);
252 return rc;
253}
254
255void xf_package_close(struct xf_package* pkg)
256{
257 if(mtar_is_open(pkg->tar))
258 mtar_close(pkg->tar);
259
260 if(pkg->alloc_handle > 0) {
261 core_free(pkg->alloc_handle);
262 pkg->alloc_handle = 0;
263 }
264}
diff --git a/lib/x1000-installer/src/xf_stream.c b/lib/x1000-installer/src/xf_stream.c
new file mode 100644
index 0000000000..5a0f86123c
--- /dev/null
+++ b/lib/x1000-installer/src/xf_stream.c
@@ -0,0 +1,214 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_stream.h"
23#include "xf_error.h"
24#include "file.h"
25#include <stdbool.h>
26#include <string.h>
27#include <ctype.h>
28
29/*
30 * File streams
31 */
32
33static off_t file_stream_get_size(struct xf_stream* s)
34{
35 return filesize(s->data);
36}
37
38static ssize_t file_stream_read(struct xf_stream* s, void* buf, size_t len)
39{
40 return read(s->data, buf, len);
41}
42
43static ssize_t file_stream_write(struct xf_stream* s, const void* buf, size_t len)
44{
45 return write(s->data, buf, len);
46}
47
48static int file_stream_close(struct xf_stream* s)
49{
50 return close(s->data);
51}
52
53static const struct xf_stream_ops file_stream_ops = {
54 .xget_size = file_stream_get_size,
55 .xread = file_stream_read,
56 .xwrite = file_stream_write,
57 .xclose = file_stream_close,
58};
59
60int xf_open_file(const char* file, int flags, struct xf_stream* s)
61{
62 s->data = open(file, flags, 0666);
63 s->ops = &file_stream_ops;
64 return (s->data >= 0) ? 0 : -1;
65}
66
67/*
68 * Tar streams
69 */
70
71static off_t tar_stream_get_size(struct xf_stream* s)
72{
73 mtar_t* mtar = (mtar_t*)s->data;
74 return mtar_get_header(mtar)->size;
75}
76
77static ssize_t tar_stream_read(struct xf_stream* s, void* buffer, size_t count)
78{
79 mtar_t* mtar = (mtar_t*)s->data;
80
81 int ret = mtar_read_data(mtar, buffer, count);
82 if(ret < 0)
83 return -1;
84
85 return ret;
86}
87
88static ssize_t tar_stream_write(struct xf_stream* s, const void* buffer, size_t count)
89{
90 mtar_t* mtar = (mtar_t*)s->data;
91
92 int ret = mtar_write_data(mtar, buffer, count);
93 if(ret < 0)
94 return -1;
95
96 return ret;
97}
98
99static int tar_stream_close(struct xf_stream* s)
100{
101 mtar_t* mtar = (mtar_t*)s->data;
102
103 if(mtar_access_mode(mtar) == MTAR_WRITE) {
104 if(mtar_update_file_size(mtar) != MTAR_ESUCCESS)
105 return -1;
106 if(mtar_end_data(mtar) != MTAR_ESUCCESS)
107 return -1;
108 }
109
110 return 0;
111}
112
113static const struct xf_stream_ops tar_stream_ops = {
114 .xget_size = tar_stream_get_size,
115 .xread = tar_stream_read,
116 .xwrite = tar_stream_write,
117 .xclose = tar_stream_close,
118};
119
120int xf_open_tar(mtar_t* mtar, const char* file, struct xf_stream* s)
121{
122 int err = mtar_find(mtar, file);
123 if(err != MTAR_ESUCCESS)
124 return err;
125
126 /* must only read normal files */
127 const mtar_header_t* h = mtar_get_header(mtar);
128 if(h->type != 0 && h->type != MTAR_TREG)
129 return MTAR_EFAILURE;
130
131 s->data = (intptr_t)mtar;
132 s->ops = &tar_stream_ops;
133 return MTAR_ESUCCESS;
134}
135
136int xf_create_tar(mtar_t* mtar, const char* file, size_t size, struct xf_stream* s)
137{
138 int err = mtar_write_file_header(mtar, file, size);
139 if(err)
140 return err;
141
142 s->data = (intptr_t)mtar;
143 s->ops = &tar_stream_ops;
144 return MTAR_ESUCCESS;
145}
146
147/*
148 * Utility functions
149 */
150
151int xf_stream_read_lines(struct xf_stream* s, char* buf, size_t bufsz,
152 int(*callback)(int n, char* buf, void* arg), void* arg)
153{
154 char* startp, *endp;
155 ssize_t bytes_read;
156 int rc;
157
158 int n = 0;
159 size_t pos = 0;
160 bool at_eof = false;
161
162 while(!at_eof) {
163 bytes_read = xf_stream_read(s, &buf[pos], bufsz - pos - 1);
164 if(bytes_read < 0)
165 return XF_E_IO;
166
167 /* short read is end of file */
168 if((size_t)bytes_read < bufsz - pos - 1)
169 at_eof = true;
170
171 pos += bytes_read;
172 buf[pos] = '\0';
173
174 startp = endp = buf;
175 while(endp != &buf[pos]) {
176 endp = strchr(startp, '\n');
177 if(endp) {
178 *endp = '\0';
179 } else {
180 if(!at_eof) {
181 if(startp == buf)
182 return XF_E_LINE_TOO_LONG;
183 else
184 break; /* read ahead to look for newline */
185 } else {
186 endp = &buf[pos]; /* treat EOF as a newline */
187 }
188 }
189
190 /* skip whitespace */
191 while(*startp && isspace(*startp))
192 ++startp;
193
194 /* ignore blank lines and comment lines */
195 if(*startp == '#' || *startp == '\0') {
196 startp = endp + 1;
197 continue;
198 }
199
200 rc = callback(n++, startp, arg);
201 if(rc != 0)
202 return rc;
203
204 startp = endp + 1;
205 }
206
207 if(startp <= &buf[pos]) {
208 memmove(buf, startp, &buf[pos] - startp);
209 pos = &buf[pos] - startp;
210 }
211 }
212
213 return 0;
214}
diff --git a/lib/x1000-installer/src/xf_update.c b/lib/x1000-installer/src/xf_update.c
new file mode 100644
index 0000000000..5a7c3b0430
--- /dev/null
+++ b/lib/x1000-installer/src/xf_update.c
@@ -0,0 +1,149 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_update.h"
23#include "xf_error.h"
24#include "file.h"
25#include "md5.h"
26#include <string.h>
27
28static int process_stream(struct xf_nandio* nio,
29 struct xf_map* map,
30 struct xf_stream* stream)
31{
32 void* buffer;
33 size_t count;
34 int rc;
35
36 /* calculate MD5 on the fly if taking a backup */
37 md5_context md5_ctx;
38 if(nio->mode == XF_NANDIO_READ)
39 md5_starts(&md5_ctx);
40
41 /* first deal with the file data */
42 size_t bytes_left = map->length;
43 while(bytes_left > 0) {
44 count = bytes_left;
45 rc = xf_nandio_get_buffer(nio, &buffer, &count);
46 if(rc)
47 return rc;
48
49 if(nio->mode == XF_NANDIO_READ) {
50 md5_update(&md5_ctx, buffer, count);
51 rc = xf_stream_write(stream, buffer, count);
52 } else {
53 rc = xf_stream_read(stream, buffer, count);
54 }
55
56 bytes_left -= count;
57
58 if(rc < 0 || (size_t)rc > count)
59 return XF_E_IO;
60
61 if((size_t)rc < count) {
62 /* backup - we could not write all the data */
63 if(nio->mode == XF_NANDIO_READ)
64 return XF_E_IO;
65
66 /* update - clear rest of buffer to 0xff */
67 memset(buffer + rc, 0xff, count - rc);
68 break;
69 }
70 }
71
72 /* if updating - write blanks to the remainder of the region */
73 while(bytes_left > 0) {
74 count = bytes_left;
75 rc = xf_nandio_get_buffer(nio, &buffer, &count);
76 if(rc)
77 return rc;
78
79 memset(buffer, 0xff, count);
80 bytes_left -= count;
81 }
82
83 /* finalize the MD5 sum */
84 if(nio->mode == XF_NANDIO_READ) {
85 md5_finish(&md5_ctx, map->md5);
86 map->file_length = map->length;
87 map->flags |= XF_MAP_HAS_MD5 | XF_MAP_HAS_FILE_LENGTH;
88 }
89
90 return XF_E_SUCCESS;
91}
92
93static int process_map(struct xf_nandio* nio, struct xf_map* map, int map_size,
94 xf_update_open_stream_cb open_stream, void* os_arg)
95{
96 int rc, rc2;
97 struct xf_stream stream;
98
99 /* ensure the map is sequential and non-overlapping before continuing */
100 if(xf_map_validate(map, map_size) != 0)
101 return XF_E_INVALID_PARAMETER;
102
103 for(int i = 0; i < map_size; ++i) {
104 /* seek to initial offset */
105 rc = xf_nandio_seek(nio, map[i].offset);
106 if(rc)
107 return rc;
108
109 rc = open_stream(os_arg, map[i].name, &stream);
110 if(rc)
111 return XF_E_CANNOT_OPEN_FILE;
112
113 /* process the stream and be sure to close it even on error */
114 rc = process_stream(nio, &map[i], &stream);
115 rc2 = xf_stream_close(&stream);
116
117 /* bail if either operation raised an error */
118 if(rc)
119 return rc;
120 if(rc2)
121 return rc2;
122 }
123
124 /* now flush to ensure all data was written */
125 rc = xf_nandio_flush(nio);
126 if(rc)
127 return rc;
128
129 return XF_E_SUCCESS;
130}
131
132static const enum xf_nandio_mode update_mode_to_nandio[] = {
133 [XF_UPDATE] = XF_NANDIO_PROGRAM,
134 [XF_BACKUP] = XF_NANDIO_READ,
135 [XF_VERIFY] = XF_NANDIO_VERIFY,
136};
137
138int xf_updater_run(enum xf_update_mode mode, struct xf_nandio* nio,
139 struct xf_map* map, int map_size,
140 xf_update_open_stream_cb open_stream, void* arg)
141{
142 /* Switch NAND I/O into the correct mode */
143 int rc = xf_nandio_set_mode(nio, update_mode_to_nandio[mode]);
144 if(rc)
145 return rc;
146
147 /* This does all the real work */
148 return process_map(nio, map, map_size, open_stream, arg);
149}
diff --git a/lib/x1000-installer/test/main.c b/lib/x1000-installer/test/main.c
new file mode 100644
index 0000000000..e952fb9d86
--- /dev/null
+++ b/lib/x1000-installer/test/main.c
@@ -0,0 +1,98 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "xf_nandio.h"
23#include "xf_error.h"
24#include "xf_update.h"
25#include "file.h"
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30static struct xf_nandio nio;
31
32static struct xf_map testmap[] = {
33 {
34 .name = "src/xf_error.c",
35 .offset = 0x00000000,
36 .length = 0x00004000,
37 },
38 {
39 .name = "src/xf_update.c",
40 .offset = 0x00010000,
41 .length = 0x00004000,
42 },
43 {
44 .name = "src/xf_package.c",
45 .offset = 0x00020000,
46 .length = 0x00001800,
47 },
48};
49
50void checkrc(int rc)
51{
52 if(rc == XF_E_SUCCESS)
53 return;
54
55 if(rc == XF_E_NAND) {
56 printf("NAND error: %d\n", nio.nand_err);
57 } else {
58 printf("error: %s\n", xf_strerror(rc));
59 printf(" CurBlock = %lu\n", (unsigned long)nio.cur_block);
60 printf(" CurOffset = %lu\n", (unsigned long)nio.offset_in_block);
61 }
62
63 exit(1);
64}
65
66int openstream_file(void* arg, const char* file, struct xf_stream* stream)
67{
68 (void)arg;
69 return xf_open_file(file, O_RDONLY, stream);
70}
71
72int main(int argc, char* argv[])
73{
74 (void)argc;
75 (void)argv;
76
77 nand_trace_reset(65535);
78
79 int rc = xf_nandio_init(&nio);
80 checkrc(rc);
81
82 rc = xf_updater_run(XF_UPDATE, &nio,
83 testmap, sizeof(testmap)/sizeof(struct xf_map),
84 openstream_file, NULL);
85 checkrc(rc);
86
87 for(size_t i = 0; i < nand_trace_length; ++i) {
88 const char* types[] = {"READ", "PROGRAM", "ERASE"};
89 const char* excep[] = {"NONE", "DOUBLE_PROGRAMMED", "CLEARED"};
90 printf("%s %s %lu\n",
91 types[nand_trace[i].type],
92 excep[nand_trace[i].exception],
93 (unsigned long)nand_trace[i].addr);
94 }
95
96 xf_nandio_destroy(&nio);
97 return 0;
98}
diff --git a/lib/x1000-installer/test_lib/core_alloc.c b/lib/x1000-installer/test_lib/core_alloc.c
new file mode 100644
index 0000000000..5d4edb03f7
--- /dev/null
+++ b/lib/x1000-installer/test_lib/core_alloc.c
@@ -0,0 +1,65 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "core_alloc.h"
23#include <stdlib.h>
24
25#define N_POINTERS 100
26
27static void* pointers[N_POINTERS];
28
29int core_alloc(const char* name, size_t size)
30{
31 (void)name;
32
33 void* mem = malloc(size);
34 if(!mem)
35 return -1;
36
37 for(int i = 0; i < N_POINTERS; ++i) {
38 if(pointers[i])
39 continue;
40
41 pointers[i] = mem;
42 return i + 1;
43 }
44
45 free(mem);
46 return -1;
47}
48
49int core_free(int handle)
50{
51 if(handle > 0) {
52 free(pointers[handle-1]);
53 pointers[handle-1] = NULL;
54 }
55
56 return 0;
57}
58
59void* core_get_data(int handle)
60{
61 if(handle > 0)
62 return pointers[handle-1];
63
64 return NULL;
65}
diff --git a/lib/x1000-installer/test_lib/core_alloc.h b/lib/x1000-installer/test_lib/core_alloc.h
new file mode 100644
index 0000000000..6fb06649fb
--- /dev/null
+++ b/lib/x1000-installer/test_lib/core_alloc.h
@@ -0,0 +1,33 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* fake core_alloc implementation for testing */
23
24#ifndef CORE_ALLOC_H
25#define CORE_ALLOC_H
26
27#include <stddef.h>
28
29int core_alloc(const char* name, size_t size);
30int core_free(int handle);
31void* core_get_data(int handle);
32
33#endif
diff --git a/lib/x1000-installer/test_lib/fakenand.c b/lib/x1000-installer/test_lib/fakenand.c
new file mode 100644
index 0000000000..19f1f31cfd
--- /dev/null
+++ b/lib/x1000-installer/test_lib/fakenand.c
@@ -0,0 +1,270 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "nand-x1000.h"
23#include <unistd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdio.h>
27#include <stdlib.h>
28
29const char* nand_backing_file = "fakeNAND.bin";
30const char* nand_meta_file = "fakeNAND_meta.bin";
31
32struct nand_trace* nand_trace = NULL;
33size_t nand_trace_capacity = 0;
34size_t nand_trace_length = 0;
35
36static struct nand_trace* nand_trace_cur = NULL;
37
38static int injected_err = 0;
39
40#define METAF_PROGRAMMED 1
41
42static const nand_chip fake_chip = {
43 /* ATO25D1GA */
44 .log2_ppb = 6, /* 64 pages */
45 .page_size = 2048,
46 .oob_size = 64,
47 .nr_blocks = 1024,
48};
49
50void nand_trace_reset(size_t size)
51{
52 nand_trace = realloc(nand_trace, size);
53 nand_trace_capacity = size;
54 nand_trace_length = 0;
55 nand_trace_cur = nand_trace;
56}
57
58void nand_inject_error(int rc)
59{
60 injected_err = rc;
61}
62
63nand_drv* nand_init(void)
64{
65 static bool inited = false;
66 static uint8_t scratch_buf[NAND_DRV_SCRATCHSIZE];
67 static uint8_t page_buf[NAND_DRV_MAXPAGESIZE];
68 static nand_drv d;
69
70 if(!inited) {
71 d.scratch_buf = scratch_buf;
72 d.page_buf = page_buf;
73 d.chip = &fake_chip;
74 inited = true;
75 }
76
77 return &d;
78}
79
80static void lock_assert(bool cond, const char* msg)
81{
82 if(!cond) {
83 fprintf(stderr, "%s\n", msg);
84 fflush(stderr);
85 abort();
86 }
87}
88
89void nand_lock(nand_drv* drv)
90{
91 drv->lock_count++;
92}
93
94void nand_unlock(nand_drv* drv)
95{
96 lock_assert(drv->lock_count > 0, "nand_unlock() called when not locked");
97 drv->lock_count--;
98}
99
100#define CHECK_INJECTED_ERROR \
101 do { int __err = injected_err; injected_err = 0; if(__err) return __err; } while(0)
102
103int nand_open(nand_drv* drv)
104{
105 lock_assert(drv->lock_count > 0, "nand_open(): lock not held");
106 CHECK_INJECTED_ERROR;
107
108 if(drv->refcount > 0) {
109 drv->refcount++;
110 return NAND_SUCCESS;
111 }
112
113 /* leaks an fd on error but this is only testing... */
114 drv->fd = open(nand_backing_file, O_RDWR|O_CREAT, 0644);
115 drv->metafd = open(nand_meta_file, O_RDWR|O_CREAT, 0644);
116 if(drv->fd < 0 || drv->metafd < 0)
117 goto err;
118
119 drv->ppb = 1 << drv->chip->log2_ppb;
120 drv->fpage_size = drv->chip->page_size + drv->chip->oob_size;
121
122 /* make backing file the correct size */
123 if(ftruncate(drv->fd, drv->chip->page_size * drv->ppb * drv->chip->nr_blocks) < 0)
124 goto err;
125 if(ftruncate(drv->metafd, drv->chip->nr_blocks * drv->ppb) < 0)
126 goto err;
127
128 drv->refcount++;
129 return NAND_SUCCESS;
130
131 err:
132 if(drv->fd >= 0)
133 close(drv->fd);
134 if(drv->metafd >= 0)
135 close(drv->metafd);
136 return NAND_ERR_OTHER;
137}
138
139void nand_close(nand_drv* drv)
140{
141 lock_assert(drv->lock_count > 0, "nand_close(): lock not held");
142
143 if(--drv->refcount > 0)
144 return;
145
146 close(drv->fd);
147 close(drv->metafd);
148 drv->fd = -1;
149 drv->metafd = -1;
150}
151
152static int read_meta(nand_drv* drv, nand_page_t page)
153{
154 /* probably won't fail */
155 if(lseek(drv->metafd, page, SEEK_SET) < 0)
156 return NAND_ERR_OTHER;
157 if(read(drv->metafd, drv->scratch_buf, 1) != 1)
158 return NAND_ERR_OTHER;
159
160 return drv->scratch_buf[0];
161}
162
163static int write_meta(nand_drv* drv, nand_page_t page, int val)
164{
165 drv->scratch_buf[0] = val;
166
167 if(lseek(drv->metafd, page, SEEK_SET) < 0)
168 return NAND_ERR_OTHER;
169 if(write(drv->metafd, drv->scratch_buf, 1) != 1)
170 return NAND_ERR_OTHER;
171
172 return NAND_SUCCESS;
173}
174
175static int upd_meta(nand_drv* drv, nand_page_t page, uint8_t clr, uint8_t set)
176{
177 int meta = read_meta(drv, page);
178 if(meta < 0)
179 return meta;
180
181 meta &= ~clr;
182 meta |= set;
183
184 return write_meta(drv, page, meta);
185}
186
187static int page_program(nand_drv* drv, nand_page_t page, const void* buffer,
188 uint8_t clr, uint8_t set)
189{
190 if(lseek(drv->fd, page * drv->chip->page_size, SEEK_SET) < 0)
191 return NAND_ERR_OTHER;
192 if(write(drv->fd, buffer, drv->chip->page_size) != (ssize_t)drv->chip->page_size)
193 return NAND_ERR_PROGRAM_FAIL;
194
195 return upd_meta(drv, page, clr, set);
196}
197
198static void trace(enum nand_trace_type ty, enum nand_trace_exception ex, nand_page_t addr)
199{
200 if(nand_trace_length < nand_trace_capacity) {
201 nand_trace_cur->type = ty;
202 nand_trace_cur->exception = ex;
203 nand_trace_cur->addr = addr;
204 nand_trace_cur++;
205 nand_trace_length++;
206 }
207}
208
209int nand_block_erase(nand_drv* drv, nand_block_t block)
210{
211 lock_assert(drv->lock_count > 0, "nand_block_erase(): lock not held");
212 CHECK_INJECTED_ERROR;
213
214 trace(NTT_ERASE, NTE_NONE, block);
215
216 memset(drv->page_buf, 0xff, drv->fpage_size);
217
218 for(unsigned i = 0; i < drv->ppb; ++i) {
219 int rc = page_program(drv, block + i, drv->page_buf, METAF_PROGRAMMED, 0);
220 if(rc < 0)
221 return NAND_ERR_ERASE_FAIL;
222 }
223
224 return NAND_SUCCESS;
225}
226
227int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer)
228{
229 lock_assert(drv->lock_count > 0, "nand_page_program(): lock not held");
230 CHECK_INJECTED_ERROR;
231
232 int meta = read_meta(drv, page);
233 if(meta < 0)
234 return meta;
235
236 enum nand_trace_exception exception = NTE_NONE;
237 if(meta & METAF_PROGRAMMED)
238 exception = NTE_DOUBLE_PROGRAMMED;
239
240 trace(NTT_PROGRAM, exception, page);
241
242 return page_program(drv, page, buffer, 0, METAF_PROGRAMMED);
243}
244
245int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer)
246{
247 lock_assert(drv->lock_count > 0, "nand_page_read(): lock not held");
248 CHECK_INJECTED_ERROR;
249
250 enum nand_trace_exception exception = NTE_NONE;
251
252 int meta = read_meta(drv, page);
253 if(meta < 0)
254 return meta;
255
256 if(meta & METAF_PROGRAMMED) {
257 if(lseek(drv->fd, page * drv->chip->page_size, SEEK_SET) < 0)
258 return NAND_ERR_OTHER;
259 if(read(drv->fd, buffer, drv->chip->page_size) != (ssize_t)drv->chip->page_size)
260 return NAND_ERR_OTHER;
261 } else {
262 memset(buffer, 0xff, drv->chip->page_size);
263 exception = NTE_CLEARED;
264 }
265
266 trace(NTT_READ, exception, page);
267
268 memset(buffer + drv->chip->page_size, 0xff, drv->chip->oob_size);
269 return NAND_SUCCESS;
270}
diff --git a/lib/x1000-installer/test_lib/file.c b/lib/x1000-installer/test_lib/file.c
new file mode 100644
index 0000000000..8769c009a4
--- /dev/null
+++ b/lib/x1000-installer/test_lib/file.c
@@ -0,0 +1,11 @@
1#include "file.h"
2
3off_t filesize(int osfd)
4{
5 struct stat sb;
6
7 if (!fstat(osfd, &sb))
8 return sb.st_size;
9 else
10 return -1;
11}
diff --git a/lib/x1000-installer/test_lib/file.h b/lib/x1000-installer/test_lib/file.h
new file mode 100644
index 0000000000..2a6554c695
--- /dev/null
+++ b/lib/x1000-installer/test_lib/file.h
@@ -0,0 +1,18 @@
1#ifndef FILE_H
2#define FILE_H
3
4#include <sys/types.h>
5#include <sys/statfs.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <unistd.h>
9
10#ifdef MAX_PATH
11# undef MAX_PATH
12#endif
13
14#define MAX_PATH 260
15
16off_t filesize(int fd);
17
18#endif
diff --git a/lib/x1000-installer/test_lib/md5.c b/lib/x1000-installer/test_lib/md5.c
new file mode 100644
index 0000000000..3050c7ebd8
--- /dev/null
+++ b/lib/x1000-installer/test_lib/md5.c
@@ -0,0 +1,245 @@
1/*
2 * RFC 1321 compliant MD5 implementation
3 *
4 * Copyright (C) 2001-2003 Christophe Devine
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <string.h>
22
23#include "md5.h"
24
25#define GET_UINT32(n,b,i) \
26{ \
27 (n) = ( (uint32_t) (b)[(i) ] ) \
28 | ( (uint32_t) (b)[(i) + 1] << 8 ) \
29 | ( (uint32_t) (b)[(i) + 2] << 16 ) \
30 | ( (uint32_t) (b)[(i) + 3] << 24 ); \
31}
32
33#define PUT_UINT32(n,b,i) \
34{ \
35 (b)[(i) ] = (uint8_t) ( (n) ); \
36 (b)[(i) + 1] = (uint8_t) ( (n) >> 8 ); \
37 (b)[(i) + 2] = (uint8_t) ( (n) >> 16 ); \
38 (b)[(i) + 3] = (uint8_t) ( (n) >> 24 ); \
39}
40
41void md5_starts( md5_context *ctx )
42{
43 ctx->total[0] = 0;
44 ctx->total[1] = 0;
45
46 ctx->state[0] = 0x67452301;
47 ctx->state[1] = 0xEFCDAB89;
48 ctx->state[2] = 0x98BADCFE;
49 ctx->state[3] = 0x10325476;
50}
51
52static void md5_process( md5_context *ctx, uint8_t data[64] )
53{
54 uint32_t X[16], A, B, C, D;
55
56 GET_UINT32( X[0], data, 0 );
57 GET_UINT32( X[1], data, 4 );
58 GET_UINT32( X[2], data, 8 );
59 GET_UINT32( X[3], data, 12 );
60 GET_UINT32( X[4], data, 16 );
61 GET_UINT32( X[5], data, 20 );
62 GET_UINT32( X[6], data, 24 );
63 GET_UINT32( X[7], data, 28 );
64 GET_UINT32( X[8], data, 32 );
65 GET_UINT32( X[9], data, 36 );
66 GET_UINT32( X[10], data, 40 );
67 GET_UINT32( X[11], data, 44 );
68 GET_UINT32( X[12], data, 48 );
69 GET_UINT32( X[13], data, 52 );
70 GET_UINT32( X[14], data, 56 );
71 GET_UINT32( X[15], data, 60 );
72
73#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
74
75#define P(a,b,c,d,k,s,t) \
76{ \
77 a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
78}
79
80 A = ctx->state[0];
81 B = ctx->state[1];
82 C = ctx->state[2];
83 D = ctx->state[3];
84
85#define F(x,y,z) (z ^ (x & (y ^ z)))
86
87 P( A, B, C, D, 0, 7, 0xD76AA478 );
88 P( D, A, B, C, 1, 12, 0xE8C7B756 );
89 P( C, D, A, B, 2, 17, 0x242070DB );
90 P( B, C, D, A, 3, 22, 0xC1BDCEEE );
91 P( A, B, C, D, 4, 7, 0xF57C0FAF );
92 P( D, A, B, C, 5, 12, 0x4787C62A );
93 P( C, D, A, B, 6, 17, 0xA8304613 );
94 P( B, C, D, A, 7, 22, 0xFD469501 );
95 P( A, B, C, D, 8, 7, 0x698098D8 );
96 P( D, A, B, C, 9, 12, 0x8B44F7AF );
97 P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
98 P( B, C, D, A, 11, 22, 0x895CD7BE );
99 P( A, B, C, D, 12, 7, 0x6B901122 );
100 P( D, A, B, C, 13, 12, 0xFD987193 );
101 P( C, D, A, B, 14, 17, 0xA679438E );
102 P( B, C, D, A, 15, 22, 0x49B40821 );
103
104#undef F
105
106#define F(x,y,z) (y ^ (z & (x ^ y)))
107
108 P( A, B, C, D, 1, 5, 0xF61E2562 );
109 P( D, A, B, C, 6, 9, 0xC040B340 );
110 P( C, D, A, B, 11, 14, 0x265E5A51 );
111 P( B, C, D, A, 0, 20, 0xE9B6C7AA );
112 P( A, B, C, D, 5, 5, 0xD62F105D );
113 P( D, A, B, C, 10, 9, 0x02441453 );
114 P( C, D, A, B, 15, 14, 0xD8A1E681 );
115 P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
116 P( A, B, C, D, 9, 5, 0x21E1CDE6 );
117 P( D, A, B, C, 14, 9, 0xC33707D6 );
118 P( C, D, A, B, 3, 14, 0xF4D50D87 );
119 P( B, C, D, A, 8, 20, 0x455A14ED );
120 P( A, B, C, D, 13, 5, 0xA9E3E905 );
121 P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
122 P( C, D, A, B, 7, 14, 0x676F02D9 );
123 P( B, C, D, A, 12, 20, 0x8D2A4C8A );
124
125#undef F
126
127#define F(x,y,z) (x ^ y ^ z)
128
129 P( A, B, C, D, 5, 4, 0xFFFA3942 );
130 P( D, A, B, C, 8, 11, 0x8771F681 );
131 P( C, D, A, B, 11, 16, 0x6D9D6122 );
132 P( B, C, D, A, 14, 23, 0xFDE5380C );
133 P( A, B, C, D, 1, 4, 0xA4BEEA44 );
134 P( D, A, B, C, 4, 11, 0x4BDECFA9 );
135 P( C, D, A, B, 7, 16, 0xF6BB4B60 );
136 P( B, C, D, A, 10, 23, 0xBEBFBC70 );
137 P( A, B, C, D, 13, 4, 0x289B7EC6 );
138 P( D, A, B, C, 0, 11, 0xEAA127FA );
139 P( C, D, A, B, 3, 16, 0xD4EF3085 );
140 P( B, C, D, A, 6, 23, 0x04881D05 );
141 P( A, B, C, D, 9, 4, 0xD9D4D039 );
142 P( D, A, B, C, 12, 11, 0xE6DB99E5 );
143 P( C, D, A, B, 15, 16, 0x1FA27CF8 );
144 P( B, C, D, A, 2, 23, 0xC4AC5665 );
145
146#undef F
147
148#define F(x,y,z) (y ^ (x | ~z))
149
150 P( A, B, C, D, 0, 6, 0xF4292244 );
151 P( D, A, B, C, 7, 10, 0x432AFF97 );
152 P( C, D, A, B, 14, 15, 0xAB9423A7 );
153 P( B, C, D, A, 5, 21, 0xFC93A039 );
154 P( A, B, C, D, 12, 6, 0x655B59C3 );
155 P( D, A, B, C, 3, 10, 0x8F0CCC92 );
156 P( C, D, A, B, 10, 15, 0xFFEFF47D );
157 P( B, C, D, A, 1, 21, 0x85845DD1 );
158 P( A, B, C, D, 8, 6, 0x6FA87E4F );
159 P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
160 P( C, D, A, B, 6, 15, 0xA3014314 );
161 P( B, C, D, A, 13, 21, 0x4E0811A1 );
162 P( A, B, C, D, 4, 6, 0xF7537E82 );
163 P( D, A, B, C, 11, 10, 0xBD3AF235 );
164 P( C, D, A, B, 2, 15, 0x2AD7D2BB );
165 P( B, C, D, A, 9, 21, 0xEB86D391 );
166
167#undef F
168
169 ctx->state[0] += A;
170 ctx->state[1] += B;
171 ctx->state[2] += C;
172 ctx->state[3] += D;
173}
174
175void md5_update( md5_context *ctx, uint8_t *input, uint32_t length )
176{
177 uint32_t left, fill;
178
179 if( ! length ) return;
180
181 left = ctx->total[0] & 0x3F;
182 fill = 64 - left;
183
184 ctx->total[0] += length;
185 ctx->total[0] &= 0xFFFFFFFF;
186
187 if( ctx->total[0] < length )
188 ctx->total[1]++;
189
190 if( left && length >= fill )
191 {
192 memcpy( (void *) (ctx->buffer + left),
193 (void *) input, fill );
194 md5_process( ctx, ctx->buffer );
195 length -= fill;
196 input += fill;
197 left = 0;
198 }
199
200 while( length >= 64 )
201 {
202 md5_process( ctx, input );
203 length -= 64;
204 input += 64;
205 }
206
207 if( length )
208 {
209 memcpy( (void *) (ctx->buffer + left),
210 (void *) input, length );
211 }
212}
213
214static uint8_t md5_padding[64] =
215{
216 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
217 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
219 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
220};
221
222void md5_finish( md5_context *ctx, uint8_t digest[16] )
223{
224 uint32_t last, padn;
225 uint32_t high, low;
226 uint8_t msglen[8];
227
228 high = ( ctx->total[0] >> 29 )
229 | ( ctx->total[1] << 3 );
230 low = ( ctx->total[0] << 3 );
231
232 PUT_UINT32( low, msglen, 0 );
233 PUT_UINT32( high, msglen, 4 );
234
235 last = ctx->total[0] & 0x3F;
236 padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
237
238 md5_update( ctx, md5_padding, padn );
239 md5_update( ctx, msglen, 8 );
240
241 PUT_UINT32( ctx->state[0], digest, 0 );
242 PUT_UINT32( ctx->state[1], digest, 4 );
243 PUT_UINT32( ctx->state[2], digest, 8 );
244 PUT_UINT32( ctx->state[3], digest, 12 );
245}
diff --git a/lib/x1000-installer/test_lib/md5.h b/lib/x1000-installer/test_lib/md5.h
new file mode 100644
index 0000000000..882636ed9a
--- /dev/null
+++ b/lib/x1000-installer/test_lib/md5.h
@@ -0,0 +1,18 @@
1#ifndef _MD5_H
2#define _MD5_H
3
4#include <stdint.h>
5
6typedef struct
7{
8 uint32_t total[2];
9 uint32_t state[4];
10 uint8_t buffer[64];
11}
12md5_context;
13
14void md5_starts( md5_context *ctx );
15void md5_update( md5_context *ctx, uint8_t *input, uint32_t length );
16void md5_finish( md5_context *ctx, uint8_t digest[16] );
17
18#endif /* md5.h */
diff --git a/lib/x1000-installer/test_lib/nand-x1000.h b/lib/x1000-installer/test_lib/nand-x1000.h
new file mode 100644
index 0000000000..f34f2ce026
--- /dev/null
+++ b/lib/x1000-installer/test_lib/nand-x1000.h
@@ -0,0 +1,112 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/* Stripped down fake version of X1000 NAND API for testing purposes,
23 * uses a normal file to store the data */
24
25#ifndef __NAND_X1000_H__
26#define __NAND_X1000_H__
27
28#include <stdint.h>
29#include <stddef.h>
30#include <stdbool.h>
31
32#define NAND_SUCCESS 0
33#define NAND_ERR_UNKNOWN_CHIP (-1)
34#define NAND_ERR_PROGRAM_FAIL (-2)
35#define NAND_ERR_ERASE_FAIL (-3)
36#define NAND_ERR_UNALIGNED (-4)
37#define NAND_ERR_OTHER (-5)
38#define NAND_ERR_INJECTED (-6)
39
40/* keep max page size in sync with the NAND chip table in the .c file */
41#define NAND_DRV_SCRATCHSIZE 32
42#define NAND_DRV_MAXPAGESIZE 2112
43
44typedef uint32_t nand_block_t;
45typedef uint32_t nand_page_t;
46
47enum nand_trace_type {
48 NTT_READ,
49 NTT_PROGRAM,
50 NTT_ERASE,
51};
52
53enum nand_trace_exception {
54 NTE_NONE,
55 NTE_DOUBLE_PROGRAMMED,
56 NTE_CLEARED,
57};
58
59struct nand_trace {
60 enum nand_trace_type type;
61 enum nand_trace_exception exception;
62 nand_page_t addr;
63};
64
65typedef struct nand_chip {
66 /* Base2 logarithm of the number of pages per block */
67 unsigned log2_ppb;
68
69 /* Size of a page's main / oob areas, in bytes. */
70 unsigned page_size;
71 unsigned oob_size;
72
73 /* Total number of blocks in the chip */
74 unsigned nr_blocks;
75} nand_chip;
76
77typedef struct nand_drv {
78 /* Backing file */
79 int fd;
80 int metafd;
81 int lock_count;
82
83 unsigned refcount;
84 uint8_t* scratch_buf;
85 uint8_t* page_buf;
86 const nand_chip* chip;
87 unsigned ppb;
88 unsigned fpage_size;
89} nand_drv;
90
91extern const char* nand_backing_file;
92extern const char* nand_meta_file;
93
94extern struct nand_trace* nand_trace;
95extern size_t nand_trace_capacity;
96extern size_t nand_trace_length;
97
98extern void nand_trace_reset(size_t size);
99extern void nand_inject_error(int rc);
100
101extern nand_drv* nand_init(void);
102
103extern void nand_lock(nand_drv* drv);
104extern void nand_unlock(nand_drv* drv);
105
106extern int nand_open(nand_drv* drv);
107extern void nand_close(nand_drv* drv);
108extern int nand_block_erase(nand_drv* drv, nand_block_t block);
109extern int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer);
110extern int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer);
111
112#endif /* __NAND_X1000_H__ */
diff --git a/lib/x1000-installer/test_lib/pathfuncs.c b/lib/x1000-installer/test_lib/pathfuncs.c
new file mode 100644
index 0000000000..341efd4730
--- /dev/null
+++ b/lib/x1000-installer/test_lib/pathfuncs.c
@@ -0,0 +1,130 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "pathfuncs.h"
23#include "strlcpy.h"
24#include "system.h"
25#include <string.h>
26
27static const char* GOBBLE_PATH_SEPCH(const char* p)
28{
29 int c;
30 while((c = *p) == PATH_SEPCH)
31 ++p;
32 return p;
33}
34
35static const char* GOBBLE_PATH_COMP(const char* p)
36{
37 int c;
38 while((c = *p) && c != PATH_SEPCH)
39 ++p;
40 return p;
41}
42
43/* Strips the trailing component from the path
44 * "" *nameptr->NUL, len=0: ""
45 * "/" *nameptr->/, len=1: "/"
46 * "//" *nameptr->2nd /, len=1: "/"
47 * "/a" *nameptr->/, len=1: "/"
48 * "a/" *nameptr->a, len=0: ""
49 * "/a/bc" *nameptr->/, len=2: "/a"
50 * "d" *nameptr->d, len=0: ""
51 * "ef/gh" *nameptr->e, len=2: "ef"
52 *
53 * Notes: * Interpret len=0 as ".".
54 * * In the same string, path_dirname() returns a pointer with the
55 * same or lower address as path_basename().
56 * * Pasting a separator between the returns of path_dirname() and
57 * path_basename() will result in a path equivalent to the input.
58 *
59 */
60size_t path_dirname(const char *name, const char **nameptr)
61{
62 const char *p = GOBBLE_PATH_SEPCH(name);
63 const char *q = name;
64 const char *r = p;
65
66 while (*(p = GOBBLE_PATH_COMP(p)))
67 {
68 const char *s = p;
69
70 if (!*(p = GOBBLE_PATH_SEPCH(p)))
71 break;
72
73 q = s;
74 }
75
76 if (q == name && r > name)
77 name = r, q = name--; /* root - return last slash */
78
79 *nameptr = name;
80 return q - name;
81}
82
83/* Appends one path to another, adding separators between components if needed.
84 * Return value and behavior is otherwise as strlcpy so that truncation may be
85 * detected.
86 *
87 * For basepath and component:
88 * PA_SEP_HARD adds a separator even if the base path is empty
89 * PA_SEP_SOFT adds a separator only if the base path is not empty
90 */
91size_t path_append(char *buf, const char *basepath,
92 const char *component, size_t bufsize)
93{
94 const char *base = basepath && basepath[0] ? basepath : buf;
95 if (!base)
96 return bufsize; /* won't work to get lengths from buf */
97
98 if (!buf)
99 bufsize = 0;
100
101 if (path_is_absolute(component))
102 {
103 /* 'component' is absolute; replace all */
104 basepath = component;
105 component = "";
106 }
107
108 /* if basepath is not null or empty, buffer contents are replaced,
109 otherwise buf contains the base path */
110 size_t len = base == buf ? strlen(buf) : my_strlcpy(buf, basepath, bufsize);
111
112 bool separate = false;
113
114 if (!basepath || !component)
115 separate = !len || base[len-1] != PATH_SEPCH;
116 else if (component[0])
117 separate = len && base[len-1] != PATH_SEPCH;
118
119 /* caller might lie about size of buf yet use buf as the base */
120 if (base == buf && bufsize && len >= bufsize)
121 buf[bufsize - 1] = '\0';
122
123 buf += len;
124 bufsize -= MIN(len, bufsize);
125
126 if (separate && (len++, bufsize > 0) && --bufsize > 0)
127 *buf++ = PATH_SEPCH;
128
129 return len + my_strlcpy(buf, component ?: "", bufsize);
130}
diff --git a/lib/x1000-installer/test_lib/pathfuncs.h b/lib/x1000-installer/test_lib/pathfuncs.h
new file mode 100644
index 0000000000..225b3cdd19
--- /dev/null
+++ b/lib/x1000-installer/test_lib/pathfuncs.h
@@ -0,0 +1,39 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef _PATHFUNCS_H_
22#define _PATHFUNCS_H_
23
24#include <stddef.h>
25#include <stdbool.h>
26
27#define PATH_SEPCH '/'
28
29/* return true if path begins with a root '/' component and is not NULL */
30static inline bool path_is_absolute(const char *path)
31{
32 return path && path[0] == PATH_SEPCH;
33}
34
35size_t path_dirname(const char *name, const char **nameptr);
36size_t path_append(char *buf, const char *basepath,
37 const char *component, size_t bufsize);
38
39#endif
diff --git a/lib/x1000-installer/test_lib/strlcpy.c b/lib/x1000-installer/test_lib/strlcpy.c
new file mode 100644
index 0000000000..681d917503
--- /dev/null
+++ b/lib/x1000-installer/test_lib/strlcpy.c
@@ -0,0 +1,50 @@
1/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
2
3/*
4 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <string.h>
20
21/*
22 * Copy src to string dst of size siz. At most siz-1 characters
23 * will be copied. Always NUL terminates (unless siz == 0).
24 * Returns strlen(src); if retval >= siz, truncation occurred.
25 */
26size_t
27my_strlcpy(char *dst, const char *src, size_t siz)
28{
29 char *d = dst;
30 const char *s = src;
31 size_t n = siz;
32
33 /* Copy as many bytes as will fit */
34 if (n != 0) {
35 while (--n != 0) {
36 if ((*d++ = *s++) == '\0')
37 break;
38 }
39 }
40
41 /* Not enough room in dst, add NUL and traverse rest of src */
42 if (n == 0) {
43 if (siz != 0)
44 *d = '\0'; /* NUL-terminate dst */
45 while (*s++)
46 ;
47 }
48
49 return(s - src - 1); /* count does not include NUL */
50}
diff --git a/lib/x1000-installer/test_lib/strlcpy.h b/lib/x1000-installer/test_lib/strlcpy.h
new file mode 100644
index 0000000000..0c4b16dd5a
--- /dev/null
+++ b/lib/x1000-installer/test_lib/strlcpy.h
@@ -0,0 +1,4 @@
1#ifndef STRLCPY_H
2#define STRLCPY_H
3size_t my_strlcpy(char *dst, const char *src, size_t siz);
4#endif
diff --git a/lib/x1000-installer/test_lib/system.h b/lib/x1000-installer/test_lib/system.h
new file mode 100644
index 0000000000..b0a5076ba9
--- /dev/null
+++ b/lib/x1000-installer/test_lib/system.h
@@ -0,0 +1,10 @@
1#ifndef SYSTEM_H
2#define SYSTEM_H
3
4#define CACHEALIGN_SIZE 1
5#define CACHEALIGN_BUFFER(x,y) do { } while(0)
6#define MIN(a, b) (((a)<(b))?(a):(b))
7#define ALIGN_BUFFER(ptr, size, align) do { } while(0)
8#define ALIGN_UP_P2(x, p) (((x) + ((1 << (p)) - 1)) & ~((1 << (p)) - 1))
9
10#endif
diff --git a/lib/x1000-installer/x1000-installer.make b/lib/x1000-installer/x1000-installer.make
new file mode 100644
index 0000000000..d58bd2042f
--- /dev/null
+++ b/lib/x1000-installer/x1000-installer.make
@@ -0,0 +1,21 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7#
8
9X1000INSTALLER_DIR = $(ROOTDIR)/lib/x1000-installer
10X1000INSTALLER_SRC = $(call preprocess, $(X1000INSTALLER_DIR)/SOURCES)
11X1000INSTALLER_OBJ := $(call c2obj, $(X1000INSTALLER_SRC))
12
13X1000INSTALLERLIB = $(BUILDDIR)/lib/libx1000-installer.a
14
15INCLUDES += -I$(X1000INSTALLER_DIR)/include
16OTHER_SRC += $(X1000INSTALLER_SRC)
17CORE_LIBS += $(X1000INSTALLERLIB)
18
19$(X1000INSTALLERLIB): $(X1000INSTALLER_OBJ)
20 $(SILENT)$(shell rm -f $@)
21 $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null