Skip to content

Commit 6217eb9

Browse files
authored
Merge pull request #136 from henrygab/self-uf2-large-volume
Self uf2 large volume
2 parents ff120ff + 28e09e0 commit 6217eb9

File tree

3 files changed

+105
-58
lines changed

3 files changed

+105
-58
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
*.x86_64
3737
#*.hex
3838

39+
# VSCode files
40+
.vscode/
41+
3942
# Debug files
4043
*.dSYM/
4144
*.su

src/usb/uf2/ghostfat.c

Lines changed: 101 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,29 @@ struct TextFile {
6363
//
6464
//--------------------------------------------------------------------+
6565

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
6789

6890
#define STR0(x) #x
6991
#define STR(x) STR0(x)
@@ -90,50 +112,62 @@ static struct TextFile const info[] = {
90112
// current.uf2 must be the last element and its content must be NULL
91113
{.name = "CURRENT UF2", .content = NULL},
92114
};
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
99117

100118
#define NUM_FILES (ARRAY_SIZE(info))
101119
#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+
102137

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?
107141

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)
111145

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
116147

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)
120150

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)
122155

123156

124157
static FAT_BootBlock const BootBlock = {
125158
.JumpInstruction = {0xeb, 0x3c, 0x90},
126159
.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,
135168
.SectorsPerTrack = 1,
136169
.Heads = 1,
170+
.TotalSectors32 = (BPB_TOTAL_SECTORS > 0xFFFF) ? BPB_TOTAL_SECTORS : 0,
137171
.PhysicalDriveNum = 0x80, // to match MediaDescriptor of 0xF8
138172
.ExtendedBootSig = 0x29,
139173
.VolumeSerialNumber = 0x00420042,
@@ -150,10 +184,13 @@ extern const uint32_t bootloaderConfig[];
150184
//--------------------------------------------------------------------+
151185
static inline bool is_uf2_block (UF2_Block const *bl)
152186
{
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) &&
154189
(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);
157194
}
158195

159196
// used when upgrading application
@@ -207,45 +244,49 @@ void padded_memcpy (char *dst, char const *src, int len)
207244
{
208245
for ( int i = 0; i < len; ++i )
209246
{
210-
if ( *src )
247+
if ( *src ) {
211248
*dst = *src++;
212-
else
249+
} else {
213250
*dst = ' ';
251+
}
214252
dst++;
215253
}
216254
}
217255

218256
void read_block(uint32_t block_no, uint8_t *data) {
219-
memset(data, 0, 512);
257+
memset(data, 0, BPB_SECTOR_SIZE);
220258
uint32_t sectionIdx = block_no;
221259

222260
if (block_no == 0) { // Requested boot block
223261
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
226264
// 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;
229267
// 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+
}
232271
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;
234274
// WARNING -- code presumes only one NULL .content for .UF2 file
235275
// and all non-NULL .content fit in one sector
236276
// 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) {
238279
data[i] = 0xff;
239280
}
240281
}
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;
243284
if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR)
244285
((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1;
245286
}
246-
} else if (block_no < START_CLUSTERS) { // Requested root directory sector
287+
} else if (block_no < FS_START_CLUSTERS_SECTOR) { // Requested root directory sector
247288

248-
sectionIdx -= START_ROOTDIR;
289+
sectionIdx -= FS_START_ROOTDIR_SECTOR;
249290

250291
DirEntry *d = (void *)data;
251292
int remainingEntries = DIRENTRIES_PER_SECTOR;
@@ -270,35 +311,37 @@ void read_block(uint32_t block_no, uint8_t *data) {
270311
d->createTime = __DOSTIME__;
271312
d->createDate = __DOSDATE__;
272313
d->lastAccessDate = __DOSDATE__;
273-
d->highStartCluster = startCluster >> 8;
314+
d->highStartCluster = startCluster >> 16;
274315
// DIR_WrtTime and DIR_WrtDate must be supported
275316
d->updateTime = __DOSTIME__;
276317
d->updateDate = __DOSDATE__;
277-
d->startCluster = startCluster & 0xFF;
318+
d->startCluster = startCluster & 0xFFFF;
278319
d->size = (inf->content ? strlen(inf->content) : UF2_SIZE);
279320
}
280321

281-
} else {
282-
sectionIdx -= START_CLUSTERS;
322+
} else if (block_no < BPB_TOTAL_SECTORS) {
323+
324+
sectionIdx -= FS_START_CLUSTERS_SECTOR;
283325
if (sectionIdx < NUM_FILES - 1) {
284326
memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content));
285327
} else { // generate the UF2 file data on-the-fly
286328
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);
288330
if (addr < CFG_UF2_FLASH_SIZE) {
289331
UF2_Block *bl = (void *)data;
290332
bl->magicStart0 = UF2_MAGIC_START0;
291333
bl->magicStart1 = UF2_MAGIC_START1;
292334
bl->magicEnd = UF2_MAGIC_END;
293335
bl->blockNo = sectionIdx;
294-
bl->numBlocks = (UF2_SIZE/2) / 256;
336+
bl->numBlocks = UF2_SECTORS;
295337
bl->targetAddr = addr;
296-
bl->payloadSize = 256;
338+
bl->payloadSize = UF2_FIRMWARE_BYTES_PER_SECTOR;
297339
bl->flags = UF2_FLAG_FAMILYID;
298340
bl->familyID = CFG_UF2_FAMILY_APP_ID;
299341
memcpy(bl->data, (void *)addr, bl->payloadSize);
300342
}
301343
}
344+
302345
}
303346
}
304347

@@ -310,7 +353,7 @@ void read_block(uint32_t block_no, uint8_t *data) {
310353
* Write an uf2 block wrapped by 512 sector.
311354
* @return number of bytes processed, only 3 following values
312355
* -1 : if not an uf2 block
313-
* 512 : write is successful
356+
* 512 : write is successful (BPB_SECTOR_SIZE == 512)
314357
* 0 : is busy with flashing, tinyusb stack will call write_block again with the same parameters later on
315358
*/
316359
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)
518561
}
519562
}
520563

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;
522566
}

src/usb/uf2/uf2cfg.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Family ID for updating Bootloader
88
#define CFG_UF2_FAMILY_BOOT_ID 0xd663823c
99

10-
#define CFG_UF2_NUM_BLOCKS 8000 // at least 4,1 MB for FAT16
10+
#define CFG_UF2_NUM_BLOCKS 0x10109 // just under 32MB
1111
#define CFG_UF2_FLASH_SIZE (1024*1024) // 1 MB
1212

1313
// Application Address Space

0 commit comments

Comments
 (0)