@@ -63,7 +63,29 @@ struct TextFile {
63
63
//
64
64
//--------------------------------------------------------------------+
65
65
66
- #define NUM_FAT_BLOCKS CFG_UF2_NUM_BLOCKS
66
+ #define BPB_SECTOR_SIZE ( 512)
67
+ #define BPB_SECTORS_PER_CLUSTER ( 1)
68
+ #define BPB_RESERVED_SECTORS ( 1)
69
+ #define BPB_NUMBER_OF_FATS ( 2)
70
+ #define BPB_ROOT_DIR_ENTRIES ( 64)
71
+ #define BPB_TOTAL_SECTORS CFG_UF2_NUM_BLOCKS
72
+ #define BPB_MEDIA_DESCRIPTOR_BYTE (0xF8)
73
+ #define FAT_ENTRY_SIZE (2)
74
+ #define FAT_ENTRIES_PER_SECTOR (BPB_SECTOR_SIZE / FAT_ENTRY_SIZE)
75
+ // NOTE: MS specification explicitly allows FAT to be larger than necessary
76
+ #define BPB_SECTORS_PER_FAT ( (BPB_TOTAL_SECTORS / FAT_ENTRIES_PER_SECTOR) + \
77
+ ((BPB_TOTAL_SECTORS % FAT_ENTRIES_PER_SECTOR) ? 1 : 0))
78
+ #define DIRENTRIES_PER_SECTOR (BPB_SECTOR_SIZE/sizeof(DirEntry))
79
+ #define ROOT_DIR_SECTOR_COUNT (BPB_ROOT_DIR_ENTRIES/DIRENTRIES_PER_SECTOR)
80
+
81
+ STATIC_ASSERT (BPB_SECTOR_SIZE == 512 ); // GhostFAT does not support other sector sizes (currently)
82
+ STATIC_ASSERT (BPB_SECTORS_PER_CLUSTER == 1 ); // GhostFAT presumes one sector == one cluster (for simplicity)
83
+ STATIC_ASSERT (BPB_NUMBER_OF_FATS == 2 ); // FAT highest compatibility
84
+ STATIC_ASSERT (sizeof (DirEntry ) == 32 ); // FAT requirement
85
+ STATIC_ASSERT (BPB_SECTOR_SIZE % sizeof (DirEntry ) == 0 ); // FAT requirement
86
+ STATIC_ASSERT (BPB_ROOT_DIR_ENTRIES % DIRENTRIES_PER_SECTOR == 0 ); // FAT requirement
87
+ STATIC_ASSERT (BPB_SECTOR_SIZE * BPB_SECTORS_PER_CLUSTER <= (32 * 1024 )); // FAT requirement (64k+ has known compatibility problems)
88
+ STATIC_ASSERT (FAT_ENTRIES_PER_SECTOR == 256 ); // FAT requirement
67
89
68
90
#define STR0 (x ) #x
69
91
#define STR (x ) STR0(x)
@@ -90,50 +112,62 @@ static struct TextFile const info[] = {
90
112
// current.uf2 must be the last element and its content must be NULL
91
113
{.name = "CURRENT UF2" , .content = NULL },
92
114
};
93
-
94
- // code presumes each non-UF2 file content fits in single sector
95
- // Cannot programmatically statically assert .content length
96
- // for each element above.
97
- STATIC_ASSERT (ARRAY_SIZE (indexFile ) < 512 );
98
-
115
+ STATIC_ASSERT (ARRAY_SIZE (infoUf2File ) < BPB_SECTOR_SIZE ); // GhostFAT requires files to fit in one sector
116
+ STATIC_ASSERT (ARRAY_SIZE (indexFile ) < BPB_SECTOR_SIZE ); // GhostFAT requires files to fit in one sector
99
117
100
118
#define NUM_FILES (ARRAY_SIZE(info))
101
119
#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry
120
+ #define REQUIRED_ROOT_DIRECTORY_SECTORS ( ((NUM_DIRENTRIES+1) / DIRENTRIES_PER_SECTOR) + \
121
+ (((NUM_DIRENTRIES+1) % DIRENTRIES_PER_SECTOR) ? 1 : 0))
122
+ STATIC_ASSERT (ROOT_DIR_SECTOR_COUNT >= REQUIRED_ROOT_DIRECTORY_SECTORS ); // FAT requirement -- Ensures BPB reserves sufficient entries for all files
123
+ STATIC_ASSERT (NUM_DIRENTRIES < (DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTOR_COUNT )); // FAT requirement -- end directory with unused entry
124
+ STATIC_ASSERT (NUM_DIRENTRIES < BPB_ROOT_DIR_ENTRIES ); // FAT requirement -- Ensures BPB reserves sufficient entries for all files
125
+ STATIC_ASSERT (NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR ); // GhostFAT bug workaround -- else, code overflows buffer
126
+
127
+ #define NUM_SECTORS_IN_DATA_REGION (BPB_TOTAL_SECTORS - BPB_RESERVED_SECTORS - (BPB_NUMBER_OF_FATS * BPB_SECTORS_PER_FAT) - ROOT_DIR_SECTOR_COUNT)
128
+ #define CLUSTER_COUNT (NUM_SECTORS_IN_DATA_REGION / BPB_SECTORS_PER_CLUSTER)
129
+
130
+ // Ensure cluster count results in a valid FAT16 volume!
131
+ STATIC_ASSERT ( CLUSTER_COUNT >= 0x0FF5 && CLUSTER_COUNT < 0xFFF5 );
132
+
133
+ // Many existing FAT implementations have small (1-16) off-by-one style errors
134
+ // So, avoid being within 32 of those limits for even greater compatibility.
135
+ STATIC_ASSERT ( CLUSTER_COUNT >= 0x1015 && CLUSTER_COUNT < 0xFFD5 );
136
+
102
137
103
- #define UF2_SIZE ((USER_FLASH_END-USER_FLASH_START) * 2)
104
- #define UF2_SECTORS (UF2_SIZE / 512)
105
- #define UF2_FIRST_SECTOR (NUM_FILES + 1) // WARNING -- code presumes each non-UF2 file content fits in single sector
106
- #define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1)
138
+ #define UF2_FIRMWARE_BYTES_PER_SECTOR 256
139
+ #define TRUE_USER_FLASH_SIZE (USER_FLASH_END-USER_FLASH_START)
140
+ STATIC_ASSERT (TRUE_USER_FLASH_SIZE % UF2_FIRMWARE_BYTES_PER_SECTOR == 0 ); // UF2 requirement -- overall size must be integral multiple of per-sector payload?
107
141
108
- #define RESERVED_SECTORS 1
109
- #define ROOT_DIR_SECTORS 4
110
- #define SECTORS_PER_FAT ((NUM_FAT_BLOCKS * 2 + 511) / 512 )
142
+ #define UF2_SECTORS ( (TRUE_USER_FLASH_SIZE / UF2_FIRMWARE_BYTES_PER_SECTOR) + \
143
+ ((TRUE_USER_FLASH_SIZE % UF2_FIRMWARE_BYTES_PER_SECTOR) ? 1 : 0))
144
+ #define UF2_SIZE (UF2_SECTORS * BPB_SECTOR_SIZE )
111
145
112
- #define START_FAT0 RESERVED_SECTORS
113
- #define START_FAT1 (START_FAT0 + SECTORS_PER_FAT)
114
- #define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT)
115
- #define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS)
146
+ STATIC_ASSERT (UF2_SECTORS == ((UF2_SIZE /2 ) / 256 )); // Not a requirement ... ensuring replacement of literal value is not a change
116
147
117
- // all directory entries must fit in a single sector
118
- // because otherwise current code overflows buffer
119
- #define DIRENTRIES_PER_SECTOR (512/sizeof(DirEntry))
148
+ #define UF2_FIRST_SECTOR ((NUM_FILES + 1) * BPB_SECTORS_PER_CLUSTER) // WARNING -- code presumes each non-UF2 file content fits in single sector
149
+ #define UF2_LAST_SECTOR ((UF2_FIRST_SECTOR + UF2_SECTORS - 1) * BPB_SECTORS_PER_CLUSTER)
120
150
121
- STATIC_ASSERT (NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTORS );
151
+ #define FS_START_FAT0_SECTOR BPB_RESERVED_SECTORS
152
+ #define FS_START_FAT1_SECTOR (FS_START_FAT0_SECTOR + BPB_SECTORS_PER_FAT)
153
+ #define FS_START_ROOTDIR_SECTOR (FS_START_FAT1_SECTOR + BPB_SECTORS_PER_FAT)
154
+ #define FS_START_CLUSTERS_SECTOR (FS_START_ROOTDIR_SECTOR + ROOT_DIR_SECTOR_COUNT)
122
155
123
156
124
157
static FAT_BootBlock const BootBlock = {
125
158
.JumpInstruction = {0xeb , 0x3c , 0x90 },
126
159
.OEMInfo = "UF2 UF2 " ,
127
- .SectorSize = 512 ,
128
- .SectorsPerCluster = 1 ,
129
- .ReservedSectors = RESERVED_SECTORS ,
130
- .FATCopies = 2 ,
131
- .RootDirectoryEntries = ( ROOT_DIR_SECTORS * DIRENTRIES_PER_SECTOR ) ,
132
- .TotalSectors16 = NUM_FAT_BLOCKS - 2 ,
133
- .MediaDescriptor = 0xF8 ,
134
- .SectorsPerFAT = SECTORS_PER_FAT ,
160
+ .SectorSize = BPB_SECTOR_SIZE ,
161
+ .SectorsPerCluster = BPB_SECTORS_PER_CLUSTER ,
162
+ .ReservedSectors = BPB_RESERVED_SECTORS ,
163
+ .FATCopies = BPB_NUMBER_OF_FATS ,
164
+ .RootDirectoryEntries = BPB_ROOT_DIR_ENTRIES ,
165
+ .TotalSectors16 = ( BPB_TOTAL_SECTORS > 0xFFFF ) ? 0 : BPB_TOTAL_SECTORS ,
166
+ .MediaDescriptor = BPB_MEDIA_DESCRIPTOR_BYTE ,
167
+ .SectorsPerFAT = BPB_SECTORS_PER_FAT ,
135
168
.SectorsPerTrack = 1 ,
136
169
.Heads = 1 ,
170
+ .TotalSectors32 = (BPB_TOTAL_SECTORS > 0xFFFF ) ? BPB_TOTAL_SECTORS : 0 ,
137
171
.PhysicalDriveNum = 0x80 , // to match MediaDescriptor of 0xF8
138
172
.ExtendedBootSig = 0x29 ,
139
173
.VolumeSerialNumber = 0x00420042 ,
@@ -150,10 +184,13 @@ extern const uint32_t bootloaderConfig[];
150
184
//--------------------------------------------------------------------+
151
185
static inline bool is_uf2_block (UF2_Block const * bl )
152
186
{
153
- return (bl -> magicStart0 == UF2_MAGIC_START0 ) && (bl -> magicStart1 == UF2_MAGIC_START1 ) &&
187
+ return (bl -> magicStart0 == UF2_MAGIC_START0 ) &&
188
+ (bl -> magicStart1 == UF2_MAGIC_START1 ) &&
154
189
(bl -> magicEnd == UF2_MAGIC_END ) &&
155
- (bl -> flags & UF2_FLAG_FAMILYID ) && !(bl -> flags & UF2_FLAG_NOFLASH ) &&
156
- (bl -> payloadSize == 256 ) && !(bl -> targetAddr & 0xff );
190
+ (bl -> flags & UF2_FLAG_FAMILYID ) &&
191
+ !(bl -> flags & UF2_FLAG_NOFLASH ) &&
192
+ (bl -> payloadSize == UF2_FIRMWARE_BYTES_PER_SECTOR ) &&
193
+ !(bl -> targetAddr & 0xff );
157
194
}
158
195
159
196
// used when upgrading application
@@ -207,45 +244,49 @@ void padded_memcpy (char *dst, char const *src, int len)
207
244
{
208
245
for ( int i = 0 ; i < len ; ++ i )
209
246
{
210
- if ( * src )
247
+ if ( * src ) {
211
248
* dst = * src ++ ;
212
- else
249
+ } else {
213
250
* dst = ' ' ;
251
+ }
214
252
dst ++ ;
215
253
}
216
254
}
217
255
218
256
void read_block (uint32_t block_no , uint8_t * data ) {
219
- memset (data , 0 , 512 );
257
+ memset (data , 0 , BPB_SECTOR_SIZE );
220
258
uint32_t sectionIdx = block_no ;
221
259
222
260
if (block_no == 0 ) { // Requested boot block
223
261
memcpy (data , & BootBlock , sizeof (BootBlock ));
224
- data [510 ] = 0x55 ;
225
- data [511 ] = 0xaa ;
262
+ data [510 ] = 0x55 ; // Always at offsets 510/511, even when BPB_SECTOR_SIZE is larger
263
+ data [511 ] = 0xaa ; // Always at offsets 510/511, even when BPB_SECTOR_SIZE is larger
226
264
// logval("data[0]", data[0]);
227
- } else if (block_no < START_ROOTDIR ) { // Requested FAT table sector
228
- sectionIdx -= START_FAT0 ;
265
+ } else if (block_no < FS_START_ROOTDIR_SECTOR ) { // Requested FAT table sector
266
+ sectionIdx -= FS_START_FAT0_SECTOR ;
229
267
// logval("sidx", sectionIdx);
230
- if (sectionIdx >= SECTORS_PER_FAT )
231
- sectionIdx -= SECTORS_PER_FAT ; // second FAT is same as the first...
268
+ if (sectionIdx >= BPB_SECTORS_PER_FAT ) {
269
+ sectionIdx -= BPB_SECTORS_PER_FAT ; // second FAT is same as the first...
270
+ }
232
271
if (sectionIdx == 0 ) {
233
- data [0 ] = 0xf8 ; // first FAT entry must match BPB MediaDescriptor
272
+ // first FAT entry must match BPB MediaDescriptor
273
+ data [0 ] = BPB_MEDIA_DESCRIPTOR_BYTE ;
234
274
// WARNING -- code presumes only one NULL .content for .UF2 file
235
275
// and all non-NULL .content fit in one sector
236
276
// and requires it be the last element of the array
237
- for (uint32_t i = 1 ; i < NUM_FILES * 2 + 4 ; ++ i ) {
277
+ uint32_t const end = (NUM_FILES * FAT_ENTRY_SIZE ) + (2 * FAT_ENTRY_SIZE );
278
+ for (uint32_t i = 1 ; i < end ; ++ i ) {
238
279
data [i ] = 0xff ;
239
280
}
240
281
}
241
- for (uint32_t i = 0 ; i < 256 ; ++ i ) { // Generate the FAT chain for the firmware "file"
242
- uint32_t v = sectionIdx * 256 + i ;
282
+ for (uint32_t i = 0 ; i < FAT_ENTRIES_PER_SECTOR ; ++ i ) { // Generate the FAT chain for the firmware "file"
283
+ uint32_t v = ( sectionIdx * FAT_ENTRIES_PER_SECTOR ) + i ;
243
284
if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR )
244
285
((uint16_t * )(void * )data )[i ] = v == UF2_LAST_SECTOR ? 0xffff : v + 1 ;
245
286
}
246
- } else if (block_no < START_CLUSTERS ) { // Requested root directory sector
287
+ } else if (block_no < FS_START_CLUSTERS_SECTOR ) { // Requested root directory sector
247
288
248
- sectionIdx -= START_ROOTDIR ;
289
+ sectionIdx -= FS_START_ROOTDIR_SECTOR ;
249
290
250
291
DirEntry * d = (void * )data ;
251
292
int remainingEntries = DIRENTRIES_PER_SECTOR ;
@@ -270,35 +311,37 @@ void read_block(uint32_t block_no, uint8_t *data) {
270
311
d -> createTime = __DOSTIME__ ;
271
312
d -> createDate = __DOSDATE__ ;
272
313
d -> lastAccessDate = __DOSDATE__ ;
273
- d -> highStartCluster = startCluster >> 8 ;
314
+ d -> highStartCluster = startCluster >> 16 ;
274
315
// DIR_WrtTime and DIR_WrtDate must be supported
275
316
d -> updateTime = __DOSTIME__ ;
276
317
d -> updateDate = __DOSDATE__ ;
277
- d -> startCluster = startCluster & 0xFF ;
318
+ d -> startCluster = startCluster & 0xFFFF ;
278
319
d -> size = (inf -> content ? strlen (inf -> content ) : UF2_SIZE );
279
320
}
280
321
281
- } else {
282
- sectionIdx -= START_CLUSTERS ;
322
+ } else if (block_no < BPB_TOTAL_SECTORS ) {
323
+
324
+ sectionIdx -= FS_START_CLUSTERS_SECTOR ;
283
325
if (sectionIdx < NUM_FILES - 1 ) {
284
326
memcpy (data , info [sectionIdx ].content , strlen (info [sectionIdx ].content ));
285
327
} else { // generate the UF2 file data on-the-fly
286
328
sectionIdx -= NUM_FILES - 1 ;
287
- uint32_t addr = USER_FLASH_START + sectionIdx * 256 ;
329
+ uint32_t addr = USER_FLASH_START + ( sectionIdx * UF2_FIRMWARE_BYTES_PER_SECTOR ) ;
288
330
if (addr < CFG_UF2_FLASH_SIZE ) {
289
331
UF2_Block * bl = (void * )data ;
290
332
bl -> magicStart0 = UF2_MAGIC_START0 ;
291
333
bl -> magicStart1 = UF2_MAGIC_START1 ;
292
334
bl -> magicEnd = UF2_MAGIC_END ;
293
335
bl -> blockNo = sectionIdx ;
294
- bl -> numBlocks = ( UF2_SIZE / 2 ) / 256 ;
336
+ bl -> numBlocks = UF2_SECTORS ;
295
337
bl -> targetAddr = addr ;
296
- bl -> payloadSize = 256 ;
338
+ bl -> payloadSize = UF2_FIRMWARE_BYTES_PER_SECTOR ;
297
339
bl -> flags = UF2_FLAG_FAMILYID ;
298
340
bl -> familyID = CFG_UF2_FAMILY_APP_ID ;
299
341
memcpy (bl -> data , (void * )addr , bl -> payloadSize );
300
342
}
301
343
}
344
+
302
345
}
303
346
}
304
347
@@ -310,7 +353,7 @@ void read_block(uint32_t block_no, uint8_t *data) {
310
353
* Write an uf2 block wrapped by 512 sector.
311
354
* @return number of bytes processed, only 3 following values
312
355
* -1 : if not an uf2 block
313
- * 512 : write is successful
356
+ * 512 : write is successful (BPB_SECTOR_SIZE == 512)
314
357
* 0 : is busy with flashing, tinyusb stack will call write_block again with the same parameters later on
315
358
*/
316
359
int write_block (uint32_t block_no , uint8_t * data , WriteState * state )
@@ -518,5 +561,6 @@ int write_block (uint32_t block_no, uint8_t *data, WriteState *state)
518
561
}
519
562
}
520
563
521
- return 512 ;
564
+ STATIC_ASSERT (BPB_SECTOR_SIZE == 512 ); // if sector size changes, may need to re-validate this code
565
+ return BPB_SECTOR_SIZE ;
522
566
}
0 commit comments