1 /*
2  * Copyright (c) 2024, sakumisu
3  * Copyright (c) 2024, Egahp
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 #include "bootuf2.h"
8 #include "usbd_core.h"
9 
10 char file_INFO[] = {
11     "CherryUSB UF2 BOOT\r\n"
12     "Model: " CONFIG_PRODUCT "\r\n"
13     "Board-ID: " CONFIG_BOARD "\r\n"
14 };
15 
16 const char file_IDEX[] = {
17     "<!doctype html>\n"
18     "<html>"
19     "<body>"
20     "<script>\n"
21     "location.replace(\"" CONFIG_BOOTUF2_INDEX_URL "\");\n"
22     "</script>"
23     "</body>"
24     "</html>\n"
25 };
26 
27 const char file_JOIN[] = {
28     "<!doctype html>\n"
29     "<html>"
30     "<body>"
31     "<script>\n"
32     "location.replace(\"" CONFIG_BOOTUF2_JOIN_URL "\");\n"
33     "</script>"
34     "</body>"
35     "</html>\n"
36 };
37 
38 const char file_ID__[12] = BOOTUF2_FAMILYID_ARRAY;
39 
40 static struct bootuf2_FILE files[] = {
41     [0] = { .Name = file_ID__, .Content = NULL, .FileSize = 0 },
42     [1] = { .Name = "INFO_UF2TXT", .Content = file_INFO, .FileSize = sizeof(file_INFO) - 1 },
43     [2] = { .Name = "INDEX   HTM", .Content = file_IDEX, .FileSize = sizeof(file_IDEX) - 1 },
44     [3] = { .Name = "JOIN    HTM", .Content = file_JOIN, .FileSize = sizeof(file_JOIN) - 1 },
45 };
46 
47 struct bootuf2_data {
48     const struct bootuf2_DBR *const DBR;
49     struct bootuf2_STATE *const STATE;
50     uint8_t *const fbuff;
51     uint8_t *const erase;
52     size_t page_count;
53     uint8_t *const cache;
54     const size_t cache_size;
55     uint32_t cached_address;
56     size_t cached_bytes;
57 };
58 
59 /*!< define DBRs */
60 static const struct bootuf2_DBR bootuf2_DBR = {
61     .JMPInstruction = { 0xEB, 0x3C, 0x90 },
62     .OEM = "UF2 UF2 ",
63     .BPB = {
64         .BytesPerSector = CONFIG_BOOTUF2_SECTOR_SIZE,
65         .SectorsPerCluster = CONFIG_BOOTUF2_SECTOR_PER_CLUSTER,
66         .ReservedSectors = CONFIG_BOOTUF2_SECTOR_RESERVED,
67         .NumberOfFAT = CONFIG_BOOTUF2_NUM_OF_FAT,
68         .RootEntries = CONFIG_BOOTUF2_ROOT_ENTRIES,
69         .Sectors = (BOOTUF2_SECTORS(0) > 0xFFFF) ? 0 : BOOTUF2_SECTORS(0),
70         .MediaDescriptor = 0xF8,
71         .SectorsPerFAT = BOOTUF2_SECTORS_PER_FAT(0),
72         .SectorsPerTrack = 1,
73         .Heads = 1,
74         .HiddenSectors = 0,
75         .SectorsOver32MB = (BOOTUF2_SECTORS(0) > 0xFFFF) ? BOOTUF2_SECTORS(0) : 0,
76         .BIOSDrive = 0x80,
77         .Reserved = 0,
78         .ExtendBootSignature = 0x29,
79         .VolumeSerialNumber = 0x00420042,
80         .VolumeLabel = "CHERRYUF2",
81         .FileSystem = "FAT16   ",
82     },
83 };
84 
85 /*!< define mask */
86 static uint8_t __attribute__((aligned(4))) bootuf2_mask[BOOTUF2_BLOCKSMAX / 8 + 1] = { 0 };
87 
88 /*!< define state */
89 static struct bootuf2_STATE bootuf2_STATE = {
90     .NumberOfBlock = 0,
91     .NumberOfWritten = 0,
92     .Mask = bootuf2_mask,
93     .Enable = 1,
94 };
95 
96 /*!< define flash cache */
97 static uint8_t __attribute__((aligned(4))) bootuf2_disk_cache[CONFIG_BOOTUF2_CACHE_SIZE];
98 
99 /*!< define flash buff */
100 static uint8_t __attribute__((aligned(4))) bootuf2_disk_fbuff[256];
101 
102 /*!< define erase flag buff */
103 static uint8_t __attribute__((aligned(4))) bootuf2_disk_erase[BOOTUF2_DIVCEIL(CONFIG_BOOTUF2_PAGE_COUNTMAX, 8)];
104 
105 /*!< define disk */
106 static struct bootuf2_data bootuf2_disk = {
107     .DBR = &bootuf2_DBR,
108     .STATE = &bootuf2_STATE,
109     .fbuff = bootuf2_disk_fbuff,
110     .erase = bootuf2_disk_erase,
111     .cache = bootuf2_disk_cache,
112     .cache_size = sizeof(bootuf2_disk_cache),
113 };
114 
fname_copy(char * dst,char const * src,uint16_t len)115 static void fname_copy(char *dst, char const *src, uint16_t len)
116 {
117     for (size_t i = 0; i < len; ++i) {
118         if (*src)
119             *dst++ = *src++;
120         else
121             *dst++ = ' ';
122     }
123 }
124 
fcalculate_cluster(struct bootuf2_data * ctx)125 static void fcalculate_cluster(struct bootuf2_data *ctx)
126 {
127     /*!< init files cluster */
128     uint16_t cluster_beg = 2;
129     for (int i = 0; i < ARRAY_SIZE(files); i++) {
130         files[i].ClusterBeg = cluster_beg;
131         files[i].ClusterEnd = -1 + cluster_beg +
132                               BOOTUF2_DIVCEIL(files[i].FileSize,
133                                               ctx->DBR->BPB.BytesPerSector *
134                                                   ctx->DBR->BPB.SectorsPerCluster);
135         cluster_beg = files[i].ClusterEnd + 1;
136     }
137 }
138 
ffind_by_cluster(uint32_t cluster)139 static int ffind_by_cluster(uint32_t cluster)
140 {
141     if (cluster >= 0xFFF0) {
142         return -1;
143     }
144 
145     for (uint32_t i = 0; i < ARRAY_SIZE(files); i++) {
146         if ((files[i].ClusterBeg <= cluster) &&
147             (cluster <= files[i].ClusterEnd)) {
148             return i;
149         }
150     }
151 
152     return -1;
153 }
154 
bootuf2block_check_writable(struct bootuf2_STATE * STATE,struct bootuf2_BLOCK * uf2,uint32_t block_max)155 static bool bootuf2block_check_writable(struct bootuf2_STATE *STATE,
156                                         struct bootuf2_BLOCK *uf2, uint32_t block_max)
157 {
158     if (uf2->NumberOfBlock) {
159         if (uf2->BlockIndex < block_max) {
160             uint8_t mask = 1 << (uf2->BlockIndex % 8);
161             uint32_t pos = uf2->BlockIndex / 8;
162 
163             if ((STATE->Mask[pos] & mask) == 0) {
164                 return true;
165             }
166         }
167     }
168 
169     return false;
170 }
171 
bootuf2block_state_update(struct bootuf2_STATE * STATE,struct bootuf2_BLOCK * uf2,uint32_t block_max)172 static void bootuf2block_state_update(struct bootuf2_STATE *STATE,
173                                       struct bootuf2_BLOCK *uf2, uint32_t block_max)
174 {
175     if (uf2->NumberOfBlock) {
176         if (STATE->NumberOfBlock != uf2->NumberOfBlock) {
177             if ((uf2->NumberOfBlock >= BOOTUF2_BLOCKSMAX) ||
178                 STATE->NumberOfBlock) {
179                 /*!< uf2 block only can be update once */
180                 /*!< this will cause never auto reboot */
181                 STATE->NumberOfBlock = 0xffffffff;
182             } else {
183                 STATE->NumberOfBlock = uf2->NumberOfBlock;
184             }
185         }
186 
187         if (uf2->BlockIndex < block_max) {
188             uint8_t mask = 1 << (uf2->BlockIndex % 8);
189             uint32_t pos = uf2->BlockIndex / 8;
190 
191             if ((STATE->Mask[pos] & mask) == 0) {
192                 STATE->Mask[pos] |= mask;
193                 STATE->NumberOfWritten++;
194             }
195         }
196     }
197 
198     USB_LOG_DBG("UF2 block total %d written %d index %d\r\n",
199                 uf2->NumberOfBllock, STATE->NumberOfWritten, uf2->BlockIndex);
200 }
201 
bootuf2block_state_check(struct bootuf2_STATE * STATE)202 static bool bootuf2block_state_check(struct bootuf2_STATE *STATE)
203 {
204     return (STATE->NumberOfWritten >= STATE->NumberOfBlock) &&
205            STATE->NumberOfBlock;
206 }
207 
bootuf2_flash_flush(struct bootuf2_data * ctx)208 static int bootuf2_flash_flush(struct bootuf2_data *ctx)
209 {
210     int err;
211 
212     if (ctx->cached_bytes == 0) {
213         return 0;
214     }
215 
216     err = bootuf2_flash_write(ctx->cached_address, ctx->cache, ctx->cached_bytes);
217 
218     if (err) {
219         USB_LOG_ERR("UF2 slot flash write error %d at offset %08lx len %d\r\n",
220                     err, ctx->cached_address, ctx->cached_bytes);
221         return -1;
222     }
223 
224     ctx->cached_bytes = 0;
225 
226     return 0;
227 }
228 
bootuf2_flash_write_internal(struct bootuf2_data * ctx,struct bootuf2_BLOCK * uf2)229 int bootuf2_flash_write_internal(struct bootuf2_data *ctx, struct bootuf2_BLOCK *uf2)
230 {
231     /*!< 1.cache not empty and address not continue */
232     /*!< 2.cache full */
233     if ((ctx->cached_bytes && ((ctx->cached_address + ctx->cached_bytes) != uf2->TargetAddress)) ||
234         (ctx->cached_bytes == ctx->cache_size)) {
235         int err = bootuf2_flash_flush(ctx);
236         if (err)
237             return err;
238     }
239 
240     /*!< write len always is 256, cache_size always is a multiple of 256 */
241     memcpy(ctx->cache + ctx->cached_bytes, uf2->Data, uf2->PayloadSize);
242 
243     ctx->cached_address = uf2->TargetAddress - ctx->cached_bytes;
244     ctx->cached_bytes += uf2->PayloadSize;
245 
246     return 0;
247 }
248 
bootuf2_init(void)249 void bootuf2_init(void)
250 {
251     struct bootuf2_data *ctx;
252 
253     ctx = &bootuf2_disk;
254 
255     fcalculate_cluster(ctx);
256 
257     ctx->cached_bytes = 0;
258     ctx->cached_address = 0;
259 }
260 
boot2uf2_read_sector(uint32_t start_sector,uint8_t * buff,uint32_t sector_count)261 int boot2uf2_read_sector(uint32_t start_sector, uint8_t *buff, uint32_t sector_count)
262 {
263     struct bootuf2_data *ctx;
264 
265     ctx = &bootuf2_disk;
266 
267     while (sector_count) {
268         memset(buff, 0, ctx->DBR->BPB.BytesPerSector);
269 
270         uint32_t sector_relative = start_sector;
271 
272         /*!< DBR sector */
273         if (start_sector == BOOTUF2_SECTOR_DBR_END) {
274             memcpy(buff, ctx->DBR, sizeof(struct bootuf2_DBR));
275             buff[510] = 0x55;
276             buff[511] = 0xaa;
277         }
278         /*!< FAT sector */
279         else if (start_sector < BOOTUF2_SECTOR_FAT_END(ctx->DBR)) {
280             uint16_t *buff16 = (uint16_t *)buff;
281 
282             sector_relative -= BOOTUF2_SECTOR_RSVD_END(ctx->DBR);
283 
284             /*!< Perform the same operation on all FAT tables */
285             while (sector_relative >= ctx->DBR->BPB.SectorsPerFAT) {
286                 sector_relative -= ctx->DBR->BPB.SectorsPerFAT;
287             }
288 
289             uint16_t cluster_unused = files[ARRAY_SIZE(files) - 1].ClusterEnd + 1;
290             uint16_t cluster_absolute_first = sector_relative *
291                                               BOOTUF2_FAT16_PER_SECTOR(ctx->DBR);
292 
293             /*!< cluster used link to chain, or unsed */
294             for (uint16_t i = 0, cluster_absolute = cluster_absolute_first;
295                  i < BOOTUF2_FAT16_PER_SECTOR(ctx->DBR);
296                  i++, cluster_absolute++) {
297                 if (cluster_absolute >= cluster_unused)
298                     buff16[i] = 0;
299                 else
300                     buff16[i] = cluster_absolute + 1;
301             }
302 
303             /*!< cluster 0 and 1 */
304             if (sector_relative == 0) {
305                 buff[0] = ctx->DBR->BPB.MediaDescriptor;
306                 buff[1] = 0xff;
307                 buff16[1] = 0xffff;
308             }
309 
310             /*!< cluster end of file */
311             for (uint32_t i = 0; i < ARRAY_SIZE(files); i++) {
312                 uint16_t cluster_file_last = files[i].ClusterEnd;
313 
314                 if (cluster_file_last >= cluster_absolute_first) {
315                     uint16_t idx = cluster_file_last - cluster_absolute_first;
316                     if (idx < BOOTUF2_FAT16_PER_SECTOR(ctx->DBR)) {
317                         buff16[idx] = 0xffff;
318                     }
319                 }
320             }
321         }
322         /*!< root entries */
323         else if (start_sector < BOOTUF2_SECTOR_ROOT_END(ctx->DBR)) {
324             sector_relative -= BOOTUF2_SECTOR_FAT_END(ctx->DBR);
325 
326             struct bootuf2_ENTRY *ent = (void *)buff;
327             int remain_entries = BOOTUF2_ENTRY_PER_SECTOR(ctx->DBR);
328 
329             uint32_t file_index_first;
330 
331             /*!< volume label entry */
332             if (sector_relative == 0) {
333                 fname_copy(ent->Name, (char const *)ctx->DBR->BPB.VolumeLabel, 11);
334                 ent->Attribute = 0x28;
335                 ent++;
336                 remain_entries--;
337                 file_index_first = 0;
338             } else {
339                 /*!< -1 to account for volume label in first sector */
340                 file_index_first = sector_relative * BOOTUF2_ENTRY_PER_SECTOR(ctx->DBR) - 1;
341             }
342 
343             for (uint32_t idx = file_index_first;
344                  (remain_entries > 0) && (idx < ARRAY_SIZE(files));
345                  idx++, ent++) {
346                 const uint32_t cluster_beg = files[idx].ClusterBeg;
347 
348                 const struct bootuf2_FILE *f = &files[idx];
349 
350                 if ((0 == f->FileSize) &&
351                     (0 != idx)) {
352                     continue;
353                 }
354 
355                 fname_copy(ent->Name, f->Name, 11);
356                 ent->Attribute = 0x05;
357                 ent->CreateTimeTeenth = BOOTUF2_SECONDS_INT % 2 * 100;
358                 ent->CreateTime = BOOTUF2_DOS_TIME;
359                 ent->CreateDate = BOOTUF2_DOS_DATE;
360                 ent->LastAccessDate = BOOTUF2_DOS_DATE;
361                 ent->FirstClustH16 = cluster_beg >> 16;
362                 ent->UpdateTime = BOOTUF2_DOS_TIME;
363                 ent->UpdateDate = BOOTUF2_DOS_DATE;
364                 ent->FirstClustL16 = cluster_beg & 0xffff;
365                 ent->FileSize = f->FileSize;
366             }
367         }
368         /*!< data */
369         else if (start_sector < BOOTUF2_SECTOR_DATA_END(ctx->DBR)) {
370             sector_relative -= BOOTUF2_SECTOR_ROOT_END(ctx->DBR);
371 
372             int fid = ffind_by_cluster(2 + sector_relative / ctx->DBR->BPB.SectorsPerCluster);
373 
374             if (fid >= 0) {
375                 const struct bootuf2_FILE *f = &files[fid];
376 
377                 uint32_t sector_relative_file =
378                     sector_relative -
379                     (files[fid].ClusterBeg - 2) * ctx->DBR->BPB.SectorsPerCluster;
380 
381                 size_t fcontent_offset = sector_relative_file * ctx->DBR->BPB.BytesPerSector;
382                 size_t fcontent_length = f->FileSize;
383 
384                 if (fcontent_length > fcontent_offset) {
385                     const void *src = (void *)((uint8_t *)(f->Content) + fcontent_offset);
386                     size_t copy_size = fcontent_length - fcontent_offset;
387 
388                     if (copy_size > ctx->DBR->BPB.BytesPerSector) {
389                         copy_size = ctx->DBR->BPB.BytesPerSector;
390                     }
391 
392                     memcpy(buff, src, copy_size);
393                 }
394             }
395         }
396         /*!< unknown sector, ignore */
397 
398         start_sector++;
399         sector_count--;
400         buff += ctx->DBR->BPB.BytesPerSector;
401     }
402 
403     return 0;
404 }
405 
bootuf2_write_sector(uint32_t start_sector,const uint8_t * buff,uint32_t sector_count)406 int bootuf2_write_sector(uint32_t start_sector, const uint8_t *buff, uint32_t sector_count)
407 {
408     struct bootuf2_data *ctx;
409 
410     ctx = &bootuf2_disk;
411 
412     while (sector_count) {
413         struct bootuf2_BLOCK *uf2 = (void *)buff;
414 
415         if (!((uf2->MagicStart0 == BOOTUF2_MAGIC_START0) &&
416               (uf2->MagicStart1 == BOOTUF2_MAGIC_START1) &&
417               (uf2->MagicEnd == BOOTUF2_MAGIC_END) &&
418               (uf2->Flags & BOOTUF2_FLAG_FAMILID_PRESENT) &&
419               !(uf2->Flags & BOOTUF2_FLAG_NOT_MAIN_FLASH))) {
420             goto next;
421         }
422 
423         if (uf2->FamilyID == CONFIG_BOOTUF2_FAMILYID) {
424             if (bootuf2block_check_writable(ctx->STATE, uf2, CONFIG_BOOTUF2_FLASHMAX)) {
425                 bootuf2_flash_write_internal(ctx, uf2);
426                 bootuf2block_state_update(ctx->STATE, uf2, CONFIG_BOOTUF2_FLASHMAX);
427             } else {
428                 USB_LOG_DBG("UF2 block %d already written\r\n",
429                             uf2->BlockIndex);
430             }
431         } else {
432             USB_LOG_DBG("UF2 block illegal id %08x\r\n", uf2->FamilyID);
433         }
434 
435     next:
436         start_sector++;
437         sector_count--;
438         buff += ctx->DBR->BPB.BytesPerSector;
439     }
440 
441     return 0;
442 }
443 
bootuf2_get_sector_size(void)444 uint16_t bootuf2_get_sector_size(void)
445 {
446     return bootuf2_disk.DBR->BPB.BytesPerSector;
447 }
448 
bootuf2_get_sector_count(void)449 uint32_t bootuf2_get_sector_count(void)
450 {
451     return bootuf2_disk.DBR->BPB.SectorsOver32MB + bootuf2_disk.DBR->BPB.Sectors;
452 }
453 
bootuf2_is_write_done(void)454 bool bootuf2_is_write_done(void)
455 {
456     if (bootuf2block_state_check(bootuf2_disk.STATE)) {
457         bootuf2_flash_flush(&bootuf2_disk);
458         USB_LOG_DBG("UF2 update ok\r\n");
459         return true;
460     } else {
461         return false;
462     }
463 }