Skip to content

Commit 8df909f

Browse files
authored
Merge pull request #57 from henrygab/patch-6
Fix FAT bugs and increase file count limit
2 parents 1d816c5 + 66d1770 commit 8df909f

File tree

3 files changed

+291
-19
lines changed

3 files changed

+291
-19
lines changed

src/segger/Adafruit_nRF52_Bootloader.emProject

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<file file_name="../usb/uf2/uf2cfg.h" />
6464
<file file_name="../usb/uf2/uf2.h" />
6565
<file file_name="../usb/uf2/ghostfat.c" />
66+
<file file_name="../usb/uf2/macros.h" />
6667
</folder>
6768
<file file_name="../usb/msc_uf2.c" />
6869
<file file_name="../usb/usb.c" />

src/usb/uf2/ghostfat.c

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
#include "macros.h"
3+
24
#include "uf2.h"
35
#include "flash_nrf5x.h"
46
#include <string.h>
@@ -47,7 +49,6 @@ typedef struct {
4749
uint16_t startCluster;
4850
uint32_t size;
4951
} __attribute__((packed)) DirEntry;
50-
5152
STATIC_ASSERT(sizeof(DirEntry) == 32);
5253

5354
struct TextFile {
@@ -59,6 +60,7 @@ struct TextFile {
5960

6061
#define STR0(x) #x
6162
#define STR(x) STR0(x)
63+
6264
const char infoUf2File[] = //
6365
"UF2 Bootloader " UF2_VERSION "\r\n"
6466
"Model: " PRODUCT_NAME "\r\n"
@@ -76,16 +78,27 @@ const char indexFile[] = //
7678
"</body>"
7779
"</html>\n";
7880

81+
// WARNING -- code presumes only one NULL .content for .UF2 file
82+
// and requires it be the last element of the array
7983
static struct TextFile const info[] = {
8084
{.name = "INFO_UF2TXT", .content = infoUf2File},
8185
{.name = "INDEX HTM", .content = indexFile},
8286
{.name = "CURRENT UF2"},
8387
};
84-
#define NUM_INFO (sizeof(info) / sizeof(info[0]))
88+
89+
// WARNING -- code presumes each non-UF2 file content fits in single sector
90+
// Cannot programmatically statically assert .content length
91+
// for each element above.
92+
STATIC_ASSERT(ARRAY_SIZE2(indexFile) < 512);
93+
94+
95+
#define NUM_FILES (ARRAY_SIZE2(info))
96+
#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry
97+
8598

8699
#define UF2_SIZE (current_flash_size() * 2)
87100
#define UF2_SECTORS (UF2_SIZE / 512)
88-
#define UF2_FIRST_SECTOR (NUM_INFO + 1)
101+
#define UF2_FIRST_SECTOR (NUM_FILES + 1) // WARNING -- code presumes each non-UF2 file content fits in single sector
89102
#define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1)
90103

91104
#define RESERVED_SECTORS 1
@@ -97,14 +110,21 @@ static struct TextFile const info[] = {
97110
#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT)
98111
#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS)
99112

113+
// all directory entries must fit in a single sector
114+
// because otherwise current code overflows buffer
115+
#define DIRENTRIES_PER_SECTOR (512/sizeof(DirEntry))
116+
117+
STATIC_ASSERT(NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTORS);
118+
119+
100120
static FAT_BootBlock const BootBlock = {
101121
.JumpInstruction = {0xeb, 0x3c, 0x90},
102122
.OEMInfo = "UF2 UF2 ",
103123
.SectorSize = 512,
104124
.SectorsPerCluster = 1,
105125
.ReservedSectors = RESERVED_SECTORS,
106126
.FATCopies = 2,
107-
.RootDirectoryEntries = (ROOT_DIR_SECTORS * 512 / 32),
127+
.RootDirectoryEntries = (ROOT_DIR_SECTORS * DIRENTRIES_PER_SECTOR),
108128
.TotalSectors16 = NUM_FAT_BLOCKS - 2,
109129
.MediaDescriptor = 0xF8,
110130
.SectorsPerFAT = SECTORS_PER_FAT,
@@ -191,7 +211,10 @@ void read_block(uint32_t block_no, uint8_t *data) {
191211
sectionIdx -= SECTORS_PER_FAT; // second FAT is same as the first...
192212
if (sectionIdx == 0) {
193213
data[0] = 0xf8; // first FAT entry must match BPB MediaDescriptor
194-
for (int i = 1; i < NUM_INFO * 2 + 4; ++i) {
214+
// WARNING -- code presumes only one NULL .content for .UF2 file
215+
// and all non-NULL .content fit in one sector
216+
// and requires it be the last element of the array
217+
for (int i = 1; i < NUM_FILES * 2 + 4; ++i) {
195218
data[i] = 0xff;
196219
}
197220
}
@@ -201,25 +224,48 @@ void read_block(uint32_t block_no, uint8_t *data) {
201224
((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1;
202225
}
203226
} else if (block_no < START_CLUSTERS) { // Requested root directory sector
227+
204228
sectionIdx -= START_ROOTDIR;
205-
if (sectionIdx == 0) { // only one sector of directory entries generated
206-
DirEntry *d = (void *)data;
229+
230+
DirEntry *d = (void *)data;
231+
int remainingEntries = DIRENTRIES_PER_SECTOR;
232+
if (sectionIdx == 0) { // volume label first
233+
// volume label is first directory entry
207234
padded_memcpy(d->name, (char const *) BootBlock.VolumeLabel, 11);
208235
d->attrs = 0x28;
209-
for (int i = 0; i < NUM_INFO; ++i) {
210-
d++;
211-
struct TextFile const *inf = &info[i];
212-
d->size = inf->content ? strlen(inf->content) : UF2_SIZE;
213-
d->startCluster = i + 2;
214-
padded_memcpy(d->name, inf->name, 11);
215-
}
216-
}
217-
} else { // else Generate the UF2 file data on-the-fly
236+
d++;
237+
remainingEntries--;
238+
}
239+
240+
for (int i = DIRENTRIES_PER_SECTOR * sectionIdx;
241+
remainingEntries > 0 && i < NUM_FILES;
242+
i++, d++) {
243+
244+
// WARNING -- code presumes all but last file take exactly one sector
245+
uint16_t startCluster = i + 2;
246+
247+
struct TextFile const * inf = &info[i];
248+
padded_memcpy(d->name, inf->name, 11);
249+
d->createTimeFine = __SECONDS_INT__ % 2 * 100;
250+
d->createTime = __DOSTIME__;
251+
d->createDate = __DOSDATE__;
252+
d->lastAccessDate = __DOSDATE__;
253+
d->highStartCluster = startCluster >> 8;
254+
// DIR_WrtTime and DIR_WrtDate must be supported
255+
d->updateTime = __DOSTIME__;
256+
d->updateDate = __DOSDATE__;
257+
d->startCluster = startCluster & 0xFF;
258+
// WARNING -- code presumes only one NULL .content for .UF2 file
259+
// and requires it be the last element of the array
260+
d->size = inf->content ? strlen(inf->content) : UF2_SIZE;
261+
}
262+
263+
} else {
218264
sectionIdx -= START_CLUSTERS;
219-
if (sectionIdx < NUM_INFO - 1) {
265+
if (sectionIdx < NUM_FILES - 1) {
220266
memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content));
221-
} else {
222-
sectionIdx -= NUM_INFO - 1;
267+
} else { // generate the UF2 file data on-the-fly
268+
sectionIdx -= NUM_FILES - 1;
223269
uint32_t addr = USER_FLASH_START + sectionIdx * 256;
224270
if (addr < USER_FLASH_START+FLASH_SIZE) {
225271
UF2_Block *bl = (void *)data;

src/usb/uf2/macros.h

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/**
2+
3+
The MIT License (MIT)
4+
5+
Copyright (c)
6+
7+
All rights reserved.
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in all
17+
copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
SOFTWARE.
26+
27+
*/
28+
29+
#ifndef ARRAYSIZE2_H
30+
#define ARRAYSIZE2_H
31+
32+
#ifndef __has_feature
33+
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
34+
#endif
35+
36+
#if __cplusplus >= 199711L
37+
#pragma message "using Ivan J. Johnson's ARRAY_SIZE2"
38+
39+
// Works on older compilers, even Visual C++ 6....
40+
// Created by Ivan J. Johnson, March 06, 2007
41+
// See http://drdobbs.com/cpp/197800525?pgno=1
42+
//
43+
// Pseudocode:
44+
// if x is not an array
45+
// issue a compile-time error
46+
// else
47+
// use the traditional (non-typesafe) C99 COUNTOF expression
48+
//
49+
// If the argument is any of:
50+
// object of class type, such as an std::vector
51+
// floating-point type
52+
// function pointer
53+
// pointer-to-member
54+
// then the first reinterpret_cast<> is not legal (compiler error)
55+
//
56+
// The type for check1 is chosen and named to help understand
57+
// the cause of the error, because the class name is likely to
58+
// appear in the compiler error message.
59+
//
60+
// If check1 succeeds, then the argument must be one of:
61+
// an integral type
62+
// an enumerated type
63+
// a pointer to an object
64+
// an array
65+
//
66+
// Check2 expands approximately to sizeof(check_type(x, &x)),
67+
// where check_type is an overloaded function.
68+
// Because this is purely a compile-time computation,
69+
// the function is never really called or even implemented,
70+
// but it lets the compiler apply overload resolution,
71+
// which allows further type discrimination.
72+
// There are three possibilities to consider:
73+
// x is an integral type or enumerated type.
74+
// In this case, neither of the two function overloads
75+
// is a match, resulting in a compiler error.
76+
// x is a pointer to an object.
77+
// In this case, the first argument to check_type()
78+
// is a pointer and the second one is a pointer-to-pointer.
79+
// The best function match is the first overload of check_type,
80+
// the one that returns an incomplete type (Is_pointer).
81+
// However, because Is_pointer is an incomplete type,
82+
// sizeof(Is_pointer) is not a valid expression,
83+
// resulting in a compiler error.
84+
// x is an array.
85+
// In this case, the first argument to check_type()
86+
// is an array and the second is a pointer-to-array.
87+
// A pointer-to-array is *NOT* convertible to a
88+
// pointer-to-pointer, so the first overload of
89+
// check_type() is not a match.
90+
// However, an array IS convertible to a pointer,
91+
// and a pointer-to-array already is a pointer.
92+
// Any pointer is convertible to a void*,
93+
// so the second function overload is a match.
94+
// That overload returns a complete type (Is_array).
95+
// Because it's a complete type,
96+
// sizeof(Is_array) is a valid expression.
97+
// Thus, the compiler has EXCLUDED every possible type
98+
// except arrays via compilation errors before reaching
99+
// the third line.
100+
// Moreover, check1 and check2 are reduced to the value zero,
101+
// while the third line is the old type-unsafe C-style macro,
102+
// now made entirely type-safe.
103+
//
104+
// Additional benefits:
105+
// The result is itself constexpr
106+
//
107+
//
108+
#define ARRAY_SIZE2(arr) ( \
109+
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(arr)) + /*check1*/ \
110+
0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + /*check2*/ \
111+
sizeof(arr) / sizeof((arr)[0]) /* eval */ \
112+
)
113+
114+
struct Bad_arg_to_COUNTOF {
115+
class Is_pointer; // incomplete
116+
class Is_array {};
117+
template <typename T>
118+
static Is_pointer check_type(const T*, const T* const*);
119+
static Is_array check_type(const void*, const void*);
120+
};
121+
122+
#elif __cplusplus >= 201103L || /* any compiler claiming C++11 support */ \
123+
_MSC_VER >= 1900 || /* Visual C++ 2015 or higher */ \
124+
__has_feature(cxx_constexpr) /* CLang versions supporting constexp */
125+
126+
#pragma message "C++11 version ARRAY_SIZE2"
127+
128+
namespace detail
129+
{
130+
template <typename T, std::size_t N>
131+
constexpr std::size_t countof(T const (&)[N]) noexcept
132+
{
133+
return N;
134+
}
135+
} // namespace detail
136+
#define ARRAY_SIZE2(arr) detail::countof(arr)
137+
138+
#elif _MSC_VER // Visual C++ fallback
139+
140+
#pragma message "using Microsoft Visual C++ intrinsic ARRAY_SIZE2"
141+
#define ARRAY_SIZE2(arr) _countof(arr)
142+
143+
#elif __cplusplus >= 199711L && ( /* C++ 98 trick */ \
144+
defined(__INTEL_COMPILER) || \
145+
defined(__clang__) || \
146+
(defined(__GNUC__) && ( \
147+
(__GNUC__ > 4) || \
148+
(__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \
149+
)))
150+
151+
#pragma message "C++98 version ARRAY_SIZE2"
152+
153+
template <typename T, std::size_t N>
154+
char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N];
155+
#define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x))
156+
157+
#else
158+
159+
#pragma message "Using type-unsafe version of ARRAY_SIZE2"
160+
// This is the worst-case scenario macro.
161+
// While it is valid C, it is NOT typesafe.
162+
// For example, if the parameter arr is a pointer instead of array,
163+
// the compiler will SILENTLY give a (likely) incorrect result.
164+
#define ARRAY_SIZE2(arr) sizeof(arr) / sizeof(arr[0])
165+
166+
#endif
167+
168+
169+
#endif // ARRAYSIZE2_H
170+
171+
172+
#ifndef COMPILE_DATE_H
173+
#define COMPILE_DATE_H
174+
175+
#define __YEAR_INT__ ((( \
176+
(__DATE__ [ 7u] - '0') * 10u + \
177+
(__DATE__ [ 8u] - '0')) * 10u + \
178+
(__DATE__ [ 9u] - '0')) * 10u + \
179+
(__DATE__ [10u] - '0'))
180+
181+
#define __MONTH_INT__ ( \
182+
(__DATE__ [2u] == 'n' && __DATE__ [1u] == 'a') ? 1u /*Jan*/ \
183+
: (__DATE__ [2u] == 'b' ) ? 2u /*Feb*/ \
184+
: (__DATE__ [2u] == 'r' && __DATE__ [1u] == 'a') ? 3u /*Mar*/ \
185+
: (__DATE__ [2u] == 'r' ) ? 4u /*Apr*/ \
186+
: (__DATE__ [2u] == 'y' ) ? 5u /*May*/ \
187+
: (__DATE__ [2u] == 'n' ) ? 6u /*Jun*/ \
188+
: (__DATE__ [2u] == 'l' ) ? 7u /*Jul*/ \
189+
: (__DATE__ [2u] == 'g' ) ? 8u /*Jul*/ \
190+
: (__DATE__ [2u] == 'p' ) ? 9u /*Jul*/ \
191+
: (__DATE__ [2u] == 't' ) ? 10u /*Jul*/ \
192+
: (__DATE__ [2u] == 'v' ) ? 11u /*Jul*/ \
193+
: 12u /*Dec*/ )
194+
195+
#define __DAY_INT__ ( \
196+
(__DATE__ [4u] == ' ' ? 0u : __DATE__ [4u] - '0') * 10u \
197+
+ (__DATE__ [5u] - '0') )
198+
199+
// __TIME__ expands to an eight-character string constant
200+
// "23:59:01", or (if cannot determine time) "??:??:??"
201+
#define __HOUR_INT__ ( \
202+
(__TIME__ [0u] == '?' ? 0u : __TIME__ [0u] - '0') * 10u \
203+
+ (__TIME__ [1u] == '?' ? 0u : __TIME__ [1u] - '0') )
204+
205+
#define __MINUTE_INT__ ( \
206+
(__TIME__ [3u] == '?' ? 0u : __TIME__ [3u] - '0') * 10u \
207+
+ (__TIME__ [4u] == '?' ? 0u : __TIME__ [4u] - '0') )
208+
209+
#define __SECONDS_INT__ ( \
210+
(__TIME__ [6u] == '?' ? 0u : __TIME__ [6u] - '0') * 10u \
211+
+ (__TIME__ [7u] == '?' ? 0u : __TIME__ [7u] - '0') )
212+
213+
214+
#define __DOSDATE__ ( \
215+
((__YEAR_INT__ - 1980u) << 9u) | \
216+
( __MONTH_INT__ << 5u) | \
217+
( __DAY_INT__ << 0u) )
218+
219+
#define __DOSTIME__ ( \
220+
( __HOUR_INT__ << 11u) | \
221+
( __MONTH_INT__ << 5u) | \
222+
( __DAY_INT__ << 0u) )
223+
224+
#endif // COMPILE_DATE_H
225+

0 commit comments

Comments
 (0)