123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /*
- * Copyright (c) 2024, sakumisu
- * Copyright (c) 2024, Egahp
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include "bootuf2.h"
- #include "usbd_core.h"
- char file_INFO[] = {
- "CherryUSB UF2 BOOT\r\n"
- "Model: " CONFIG_PRODUCT "\r\n"
- "Board-ID: " CONFIG_BOARD "\r\n"
- };
- const char file_IDEX[] = {
- "<!doctype html>\n"
- "<html>"
- "<body>"
- "<script>\n"
- "location.replace(\"" CONFIG_BOOTUF2_INDEX_URL "\");\n"
- "</script>"
- "</body>"
- "</html>\n"
- };
- const char file_JOIN[] = {
- "<!doctype html>\n"
- "<html>"
- "<body>"
- "<script>\n"
- "location.replace(\"" CONFIG_BOOTUF2_JOIN_URL "\");\n"
- "</script>"
- "</body>"
- "</html>\n"
- };
- const char file_ID__[12] = BOOTUF2_FAMILYID_ARRAY;
- static struct bootuf2_FILE files[] = {
- [0] = { .Name = file_ID__, .Content = NULL, .FileSize = 0 },
- [1] = { .Name = "INFO_UF2TXT", .Content = file_INFO, .FileSize = sizeof(file_INFO) - 1 },
- [2] = { .Name = "INDEX HTM", .Content = file_IDEX, .FileSize = sizeof(file_IDEX) - 1 },
- [3] = { .Name = "JOIN HTM", .Content = file_JOIN, .FileSize = sizeof(file_JOIN) - 1 },
- };
- struct bootuf2_data {
- const struct bootuf2_DBR *const DBR;
- struct bootuf2_STATE *const STATE;
- uint8_t *const fbuff;
- uint8_t *const erase;
- size_t page_count;
- uint8_t *const cache;
- const size_t cache_size;
- uint32_t cached_address;
- size_t cached_bytes;
- };
- /*!< define DBRs */
- static const struct bootuf2_DBR bootuf2_DBR = {
- .JMPInstruction = { 0xEB, 0x3C, 0x90 },
- .OEM = "UF2 UF2 ",
- .BPB = {
- .BytesPerSector = CONFIG_BOOTUF2_SECTOR_SIZE,
- .SectorsPerCluster = CONFIG_BOOTUF2_SECTOR_PER_CLUSTER,
- .ReservedSectors = CONFIG_BOOTUF2_SECTOR_RESERVED,
- .NumberOfFAT = CONFIG_BOOTUF2_NUM_OF_FAT,
- .RootEntries = CONFIG_BOOTUF2_ROOT_ENTRIES,
- .Sectors = (BOOTUF2_SECTORS(0) > 0xFFFF) ? 0 : BOOTUF2_SECTORS(0),
- .MediaDescriptor = 0xF8,
- .SectorsPerFAT = BOOTUF2_SECTORS_PER_FAT(0),
- .SectorsPerTrack = 1,
- .Heads = 1,
- .HiddenSectors = 0,
- .SectorsOver32MB = (BOOTUF2_SECTORS(0) > 0xFFFF) ? BOOTUF2_SECTORS(0) : 0,
- .BIOSDrive = 0x80,
- .Reserved = 0,
- .ExtendBootSignature = 0x29,
- .VolumeSerialNumber = 0x00420042,
- .VolumeLabel = "CHERRYUF2",
- .FileSystem = "FAT16 ",
- },
- };
- /*!< define mask */
- static uint8_t __attribute__((aligned(4))) bootuf2_mask[BOOTUF2_BLOCKSMAX / 8 + 1] = { 0 };
- /*!< define state */
- static struct bootuf2_STATE bootuf2_STATE = {
- .NumberOfBlock = 0,
- .NumberOfWritten = 0,
- .Mask = bootuf2_mask,
- .Enable = 1,
- };
- /*!< define flash cache */
- static uint8_t __attribute__((aligned(4))) bootuf2_disk_cache[CONFIG_BOOTUF2_CACHE_SIZE];
- /*!< define flash buff */
- static uint8_t __attribute__((aligned(4))) bootuf2_disk_fbuff[256];
- /*!< define erase flag buff */
- static uint8_t __attribute__((aligned(4))) bootuf2_disk_erase[BOOTUF2_DIVCEIL(CONFIG_BOOTUF2_PAGE_COUNTMAX, 8)];
- /*!< define disk */
- static struct bootuf2_data bootuf2_disk = {
- .DBR = &bootuf2_DBR,
- .STATE = &bootuf2_STATE,
- .fbuff = bootuf2_disk_fbuff,
- .erase = bootuf2_disk_erase,
- .cache = bootuf2_disk_cache,
- .cache_size = sizeof(bootuf2_disk_cache),
- };
- static void fname_copy(char *dst, char const *src, uint16_t len)
- {
- for (size_t i = 0; i < len; ++i) {
- if (*src)
- *dst++ = *src++;
- else
- *dst++ = ' ';
- }
- }
- static void fcalculate_cluster(struct bootuf2_data *ctx)
- {
- /*!< init files cluster */
- uint16_t cluster_beg = 2;
- for (int i = 0; i < ARRAY_SIZE(files); i++) {
- files[i].ClusterBeg = cluster_beg;
- files[i].ClusterEnd = -1 + cluster_beg +
- BOOTUF2_DIVCEIL(files[i].FileSize,
- ctx->DBR->BPB.BytesPerSector *
- ctx->DBR->BPB.SectorsPerCluster);
- cluster_beg = files[i].ClusterEnd + 1;
- }
- }
- static int ffind_by_cluster(uint32_t cluster)
- {
- if (cluster >= 0xFFF0) {
- return -1;
- }
- for (uint32_t i = 0; i < ARRAY_SIZE(files); i++) {
- if ((files[i].ClusterBeg <= cluster) &&
- (cluster <= files[i].ClusterEnd)) {
- return i;
- }
- }
- return -1;
- }
- static bool bootuf2block_check_writable(struct bootuf2_STATE *STATE,
- struct bootuf2_BLOCK *uf2, uint32_t block_max)
- {
- if (uf2->NumberOfBlock) {
- if (uf2->BlockIndex < block_max) {
- uint8_t mask = 1 << (uf2->BlockIndex % 8);
- uint32_t pos = uf2->BlockIndex / 8;
- if ((STATE->Mask[pos] & mask) == 0) {
- return true;
- }
- }
- }
- return false;
- }
- static void bootuf2block_state_update(struct bootuf2_STATE *STATE,
- struct bootuf2_BLOCK *uf2, uint32_t block_max)
- {
- if (uf2->NumberOfBlock) {
- if (STATE->NumberOfBlock != uf2->NumberOfBlock) {
- if ((uf2->NumberOfBlock >= BOOTUF2_BLOCKSMAX) ||
- STATE->NumberOfBlock) {
- /*!< uf2 block only can be update once */
- /*!< this will cause never auto reboot */
- STATE->NumberOfBlock = 0xffffffff;
- } else {
- STATE->NumberOfBlock = uf2->NumberOfBlock;
- }
- }
- if (uf2->BlockIndex < block_max) {
- uint8_t mask = 1 << (uf2->BlockIndex % 8);
- uint32_t pos = uf2->BlockIndex / 8;
- if ((STATE->Mask[pos] & mask) == 0) {
- STATE->Mask[pos] |= mask;
- STATE->NumberOfWritten++;
- }
- }
- }
- USB_LOG_DBG("UF2 block total %d written %d index %d\r\n",
- uf2->NumberOfBlock, STATE->NumberOfWritten, uf2->BlockIndex);
- }
- static bool bootuf2block_state_check(struct bootuf2_STATE *STATE)
- {
- return (STATE->NumberOfWritten >= STATE->NumberOfBlock) &&
- STATE->NumberOfBlock;
- }
- static int bootuf2_flash_flush(struct bootuf2_data *ctx)
- {
- int err;
- if (ctx->cached_bytes == 0) {
- return 0;
- }
- err = bootuf2_flash_write(ctx->cached_address, ctx->cache, ctx->cached_bytes);
- if (err) {
- USB_LOG_ERR("UF2 slot flash write error %d at offset %08lx len %d\r\n",
- err, ctx->cached_address, ctx->cached_bytes);
- return -1;
- }
- ctx->cached_bytes = 0;
- return 0;
- }
- int bootuf2_flash_write_internal(struct bootuf2_data *ctx, struct bootuf2_BLOCK *uf2)
- {
- /*!< 1.cache not empty and address not continue */
- /*!< 2.cache full */
- if ((ctx->cached_bytes && ((ctx->cached_address + ctx->cached_bytes) != uf2->TargetAddress)) ||
- (ctx->cached_bytes == ctx->cache_size)) {
- int err = bootuf2_flash_flush(ctx);
- if (err)
- return err;
- }
- /*!< write len always is 256, cache_size always is a multiple of 256 */
- memcpy(ctx->cache + ctx->cached_bytes, uf2->Data, uf2->PayloadSize);
- ctx->cached_address = uf2->TargetAddress - ctx->cached_bytes;
- ctx->cached_bytes += uf2->PayloadSize;
- return 0;
- }
- void bootuf2_init(void)
- {
- struct bootuf2_data *ctx;
- ctx = &bootuf2_disk;
- fcalculate_cluster(ctx);
- ctx->cached_bytes = 0;
- ctx->cached_address = 0;
- }
- int boot2uf2_read_sector(uint32_t start_sector, uint8_t *buff, uint32_t sector_count)
- {
- struct bootuf2_data *ctx;
- ctx = &bootuf2_disk;
- while (sector_count) {
- memset(buff, 0, ctx->DBR->BPB.BytesPerSector);
- uint32_t sector_relative = start_sector;
- /*!< DBR sector */
- if (start_sector == BOOTUF2_SECTOR_DBR_END) {
- memcpy(buff, ctx->DBR, sizeof(struct bootuf2_DBR));
- buff[510] = 0x55;
- buff[511] = 0xaa;
- }
- /*!< FAT sector */
- else if (start_sector < BOOTUF2_SECTOR_FAT_END(ctx->DBR)) {
- uint16_t *buff16 = (uint16_t *)buff;
- sector_relative -= BOOTUF2_SECTOR_RSVD_END(ctx->DBR);
- /*!< Perform the same operation on all FAT tables */
- while (sector_relative >= ctx->DBR->BPB.SectorsPerFAT) {
- sector_relative -= ctx->DBR->BPB.SectorsPerFAT;
- }
- uint16_t cluster_unused = files[ARRAY_SIZE(files) - 1].ClusterEnd + 1;
- uint16_t cluster_absolute_first = sector_relative *
- BOOTUF2_FAT16_PER_SECTOR(ctx->DBR);
- /*!< cluster used link to chain, or unsed */
- for (uint16_t i = 0, cluster_absolute = cluster_absolute_first;
- i < BOOTUF2_FAT16_PER_SECTOR(ctx->DBR);
- i++, cluster_absolute++) {
- if (cluster_absolute >= cluster_unused)
- buff16[i] = 0;
- else
- buff16[i] = cluster_absolute + 1;
- }
- /*!< cluster 0 and 1 */
- if (sector_relative == 0) {
- buff[0] = ctx->DBR->BPB.MediaDescriptor;
- buff[1] = 0xff;
- buff16[1] = 0xffff;
- }
- /*!< cluster end of file */
- for (uint32_t i = 0; i < ARRAY_SIZE(files); i++) {
- uint16_t cluster_file_last = files[i].ClusterEnd;
- if (cluster_file_last >= cluster_absolute_first) {
- uint16_t idx = cluster_file_last - cluster_absolute_first;
- if (idx < BOOTUF2_FAT16_PER_SECTOR(ctx->DBR)) {
- buff16[idx] = 0xffff;
- }
- }
- }
- }
- /*!< root entries */
- else if (start_sector < BOOTUF2_SECTOR_ROOT_END(ctx->DBR)) {
- sector_relative -= BOOTUF2_SECTOR_FAT_END(ctx->DBR);
- struct bootuf2_ENTRY *ent = (void *)buff;
- int remain_entries = BOOTUF2_ENTRY_PER_SECTOR(ctx->DBR);
- uint32_t file_index_first;
- /*!< volume label entry */
- if (sector_relative == 0) {
- fname_copy(ent->Name, (char const *)ctx->DBR->BPB.VolumeLabel, 11);
- ent->Attribute = 0x28;
- ent++;
- remain_entries--;
- file_index_first = 0;
- } else {
- /*!< -1 to account for volume label in first sector */
- file_index_first = sector_relative * BOOTUF2_ENTRY_PER_SECTOR(ctx->DBR) - 1;
- }
- for (uint32_t idx = file_index_first;
- (remain_entries > 0) && (idx < ARRAY_SIZE(files));
- idx++, ent++) {
- const uint32_t cluster_beg = files[idx].ClusterBeg;
- const struct bootuf2_FILE *f = &files[idx];
- if ((0 == f->FileSize) &&
- (0 != idx)) {
- continue;
- }
- fname_copy(ent->Name, f->Name, 11);
- ent->Attribute = 0x05;
- ent->CreateTimeTeenth = BOOTUF2_SECONDS_INT % 2 * 100;
- ent->CreateTime = BOOTUF2_DOS_TIME;
- ent->CreateDate = BOOTUF2_DOS_DATE;
- ent->LastAccessDate = BOOTUF2_DOS_DATE;
- ent->FirstClustH16 = cluster_beg >> 16;
- ent->UpdateTime = BOOTUF2_DOS_TIME;
- ent->UpdateDate = BOOTUF2_DOS_DATE;
- ent->FirstClustL16 = cluster_beg & 0xffff;
- ent->FileSize = f->FileSize;
- }
- }
- /*!< data */
- else if (start_sector < BOOTUF2_SECTOR_DATA_END(ctx->DBR)) {
- sector_relative -= BOOTUF2_SECTOR_ROOT_END(ctx->DBR);
- int fid = ffind_by_cluster(2 + sector_relative / ctx->DBR->BPB.SectorsPerCluster);
- if (fid >= 0) {
- const struct bootuf2_FILE *f = &files[fid];
- uint32_t sector_relative_file =
- sector_relative -
- (files[fid].ClusterBeg - 2) * ctx->DBR->BPB.SectorsPerCluster;
- size_t fcontent_offset = sector_relative_file * ctx->DBR->BPB.BytesPerSector;
- size_t fcontent_length = f->FileSize;
- if (fcontent_length > fcontent_offset) {
- const void *src = (void *)((uint8_t *)(f->Content) + fcontent_offset);
- size_t copy_size = fcontent_length - fcontent_offset;
- if (copy_size > ctx->DBR->BPB.BytesPerSector) {
- copy_size = ctx->DBR->BPB.BytesPerSector;
- }
- memcpy(buff, src, copy_size);
- }
- }
- }
- /*!< unknown sector, ignore */
- start_sector++;
- sector_count--;
- buff += ctx->DBR->BPB.BytesPerSector;
- }
- return 0;
- }
- int bootuf2_write_sector(uint32_t start_sector, const uint8_t *buff, uint32_t sector_count)
- {
- struct bootuf2_data *ctx;
- ctx = &bootuf2_disk;
- while (sector_count) {
- struct bootuf2_BLOCK *uf2 = (void *)buff;
- if (!((uf2->MagicStart0 == BOOTUF2_MAGIC_START0) &&
- (uf2->MagicStart1 == BOOTUF2_MAGIC_START1) &&
- (uf2->MagicEnd == BOOTUF2_MAGIC_END) &&
- (uf2->Flags & BOOTUF2_FLAG_FAMILID_PRESENT) &&
- !(uf2->Flags & BOOTUF2_FLAG_NOT_MAIN_FLASH))) {
- goto next;
- }
- if (uf2->FamilyID == CONFIG_BOOTUF2_FAMILYID) {
- if (bootuf2block_check_writable(ctx->STATE, uf2, CONFIG_BOOTUF2_FLASHMAX)) {
- bootuf2_flash_write_internal(ctx, uf2);
- bootuf2block_state_update(ctx->STATE, uf2, CONFIG_BOOTUF2_FLASHMAX);
- } else {
- USB_LOG_DBG("UF2 block %d already written\r\n",
- uf2->BlockIndex);
- }
- } else {
- USB_LOG_DBG("UF2 block illegal id %08x\r\n", uf2->FamilyID);
- }
- next:
- start_sector++;
- sector_count--;
- buff += ctx->DBR->BPB.BytesPerSector;
- }
- return 0;
- }
- uint16_t bootuf2_get_sector_size(void)
- {
- return bootuf2_disk.DBR->BPB.BytesPerSector;
- }
- uint32_t bootuf2_get_sector_count(void)
- {
- return bootuf2_disk.DBR->BPB.SectorsOver32MB + bootuf2_disk.DBR->BPB.Sectors;
- }
- bool bootuf2_is_write_done(void)
- {
- if (bootuf2block_state_check(bootuf2_disk.STATE)) {
- bootuf2_flash_flush(&bootuf2_disk);
- USB_LOG_DBG("UF2 update ok\r\n");
- return true;
- } else {
- return false;
- }
- }
|