summaryrefslogtreecommitdiff
path: root/bootloader/Core/Src/ghostfat.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootloader/Core/Src/ghostfat.c')
-rwxr-xr-xbootloader/Core/Src/ghostfat.c498
1 files changed, 498 insertions, 0 deletions
diff --git a/bootloader/Core/Src/ghostfat.c b/bootloader/Core/Src/ghostfat.c
new file mode 100755
index 0000000..d0ef21a
--- /dev/null
+++ b/bootloader/Core/Src/ghostfat.c
@@ -0,0 +1,498 @@
+// nb Ive hacked up this file incredibly, but I started from the UF2 version
+// so heres the license:
+/*
+ The MIT License (MIT)
+
+ Copyright (c) 2018 Microsoft Corp.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "main.h"
+
+#undef FLASH_PAGE_SIZE
+#define VOLUME_LABEL "PLINKY"
+#define FLASH_PAGE_SIZE 4096 // the mcu does 2k, but the SPI does 4k. lets go with 4k
+#define USER_FLASH_START 65536
+#define USER_FLASH_END (512*1024)
+#define UF2_FAMILY 0x00ff6919
+#define VALID_FLASH_ADDR(addr, sz) (USER_FLASH_START <= (addr) && (addr) + (sz) <= USER_FLASH_END)
+
+void target_flash_lock(void) {
+ HAL_FLASH_Lock();
+}
+
+void target_flash_unlock(void) {
+ HAL_FLASH_Unlock();
+}
+
+void scb_reset_system(void) {
+ HAL_NVIC_SystemReset();
+}
+
+#include "uf2.h"
+
+typedef struct {
+ uint8_t JumpInstruction[3]; // 0
+ uint8_t OEMInfo[8]; // 3
+ uint16_t SectorSize; // 0xb
+ uint8_t SectorsPerCluster; // 0xd
+ uint16_t ReservedSectors; // 0xe
+ uint8_t FATCopies; // 0x10
+ uint16_t RootDirectoryEntries; // 0x11
+ uint16_t TotalSectors16; // 0x13
+ uint8_t MediaDescriptor; // 0x15
+ uint16_t SectorsPerFAT; // 0x16
+ uint16_t SectorsPerTrack; // 0x18
+ uint16_t Heads; // 0x1a
+ uint32_t HiddenSectors; // 0x1c
+ uint32_t TotalSectors32; // 0x20
+ uint8_t PhysicalDriveNum;
+ uint8_t Reserved;
+ uint8_t ExtendedBootSig;
+ uint32_t VolumeSerialNumber;
+ uint8_t VolumeLabel[11];
+ uint8_t FilesystemIdentifier[8];
+} __attribute__((packed)) FAT_BootBlock;
+
+typedef struct {
+ char name[8];
+ char ext[3];
+ uint8_t attrs;
+ uint8_t reserved;
+ uint8_t createTimeFine;
+ uint16_t createTime;
+ uint16_t createDate;
+ uint16_t lastAccessDate;
+ uint16_t highStartCluster;
+ uint16_t updateTime;
+ uint16_t updateDate;
+ uint16_t startCluster;
+ uint32_t size;
+} __attribute__((packed)) DirEntry;
+
+void DebugLog(const char *fmt, ...);
+#define DBG(msg,...) DebugLog(msg "\r\n", __VA_ARGS__)
+
+struct UF2File {
+ const char name[11];
+ const char *content;
+ int size;
+};
+
+const char infoUf2File[] = //
+ "UF2 Bootloader v1.0.0 Plinky\r\n"
+ "Model: Plinky Synth v1.0.0\r\n"
+ "Board-ID: STM32L476-Plinky-100\r\n";
+
+const char indexFile[] = //
+ "<!doctype html>\n"
+ "<html>"
+ "<body>"
+ "<script>\n"
+ "location.replace(\"https://plinkysynth.com/fw\");\n"
+ "</script>"
+ "</body>"
+ "</html>\n";
+
+// ae - rewrite this part completely, I dont like the define mess
+// the sizes here are as reported by FAT, so for UF2 files they are double the size on the actual flash chip
+static const struct UF2File info[] = {
+ { .name = "INFO_UF2TXT", .content = infoUf2File, .size = sizeof(infoUf2File) - 1 },
+ { .name = "INDEX HTM", .content = indexFile, .size = sizeof(indexFile) - 1 },
+ { .name = "BOOTLOADUF2", .content = (void*) DELAY_BUF, .size = 128 * 1024 },
+ { .name = "CURRENT UF2", .content = (void*) 0x08010000, .size = (1024 - 128) * 1024 },
+ { .name = "PRESETS UF2", .content = (void*) 0x08080000, .size = 1024 * 1024- 4 * 1024 },
+ { .name = "CALIB UF2", .content = (void*) 0x080FF800, .size = 4 * 1024 },
+ { .name = "SAMPLE0 UF2", .content = (void*) 0x40000000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE1 UF2", .content = (void*) 0x40400000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE2 UF2", .content = (void*) 0x40800000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE3 UF2", .content = (void*) 0x40c00000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE4 UF2", .content = (void*) 0x41000000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE5 UF2", .content = (void*) 0x41400000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE6 UF2", .content = (void*) 0x41800000, .size = 8 * 1024 * 1024 },
+ { .name = "SAMPLE7 UF2", .content = (void*) 0x41c00000, .size = 8 * 1024 * 1024 },
+};
+#define NUM_FILES 14
+#define FIRST_UF2_FILE 2
+
+#define RESERVED_SECTORS 1
+#define ROOT_DIR_SECTORS 1
+#define SECTORS_PER_FAT 191 // each fat sector has 256 cluster entries = 512k of disk,
+
+#define START_FAT0 RESERVED_SECTORS // 1
+#define START_FAT1 (START_FAT0 + SECTORS_PER_FAT) // 192
+#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT) // 192+191
+#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS) // 192+192
+
+static const FAT_BootBlock BootBlock = { .JumpInstruction = { 0xeb, 0x3c, 0x90 }, .OEMInfo = "UF2 UF2 ", .SectorSize = 512,
+ .SectorsPerCluster = 4, // 2k clusters
+ .ReservedSectors = RESERVED_SECTORS, .FATCopies = 2, .RootDirectoryEntries = (ROOT_DIR_SECTORS * 512 / 32),
+ .TotalSectors16 = 0, //NUM_FAT_BLOCKS - 2,
+ .TotalSectors32 = (95 * 1024 * 1024) / 512, .MediaDescriptor = 0xF8, .SectorsPerFAT = SECTORS_PER_FAT, .SectorsPerTrack = 1, .Heads = 1,
+ .ExtendedBootSig = 0x29, .VolumeSerialNumber = 0x00420042, .VolumeLabel = VOLUME_LABEL, .FilesystemIdentifier = "FAT16 ", };
+
+static uint32_t ms;
+#ifdef FLASH_PAGE_SIZE
+#define NO_CACHE 0xffffffff
+static uint32_t flashAddr = NO_CACHE;
+static uint8_t flashBuf[FLASH_PAGE_SIZE] __attribute__((aligned(4)));
+static uint32_t lastFlush;
+
+#ifdef NEW_PINOUT
+#define SPI_PORT GPIOE
+#define SPI_CS0_PIN_ GPIO_PIN_1
+#define SPI_CS1_PIN_ GPIO_PIN_0
+#else
+#define SPI_PORT GPIOD
+#define SPI_CS0_PIN_ GPIO_PIN_0
+#define SPI_CS1_PIN_ GPIO_PIN_0
+#endif
+typedef unsigned char u8;
+typedef uint32_t u32;
+u8 cspin = SPI_CS0_PIN_;
+volatile u8 dummy;
+extern SPI_HandleTypeDef hspi2;
+#define CHECKRV(spirv, msg) if (spirv!=0 ) DebugLog("SPI ERROR %d " msg "\r\n", spirv);
+void spidelay(void) {
+ for (int i = 0; i < 10; ++i)
+ dummy++;
+}
+u8 spirxbuf[256 + 4];
+u8 spibigtx[256 + 4];
+void spi_setcs(void) {
+ SPI_PORT->BSRR = SPI_CS1_PIN_ | SPI_CS0_PIN_;
+ SPI_PORT->BRR = cspin;
+ spidelay();
+}
+void spi_clearcs(void) {
+ SPI_PORT->BSRR = SPI_CS1_PIN_ | SPI_CS0_PIN_;
+ spidelay();
+}
+void spi_setchip(u32 addr) {
+ cspin = ((addr >> 24) & 1) ? SPI_CS1_PIN_ : SPI_CS0_PIN_;
+}
+int spi_command(u8 *txbuf, int cmdlen) {
+ spi_setcs();
+ int spirv = HAL_SPI_TransmitReceive(&hspi2, (uint8_t*) txbuf, (uint8_t*) spirxbuf, cmdlen, -1);
+ spi_clearcs();
+ return spirv;
+}
+int spi_writeenable(void) {
+ u8 spitxbuf[1] = { 6 };
+ int spirv = spi_command(spitxbuf, 1);
+ CHECKRV(spirv, "spi_writeenable");
+ return spirv;
+}
+int spi_readid(void) {
+ u8 spitxbuf[6] = { 0x90, 0, 0, 0, 0, 0 };
+ int spirv = spi_command(spitxbuf, 6);
+ CHECKRV(spirv, "spi_readid");
+ return (spirv == 0) ? (spirxbuf[4] + (spirxbuf[5] << 8)) : -1;
+}
+int spi_readstatus(void) {
+ u8 spitxbuf[2] = { 5, 0 };
+ int spirv = spi_command(spitxbuf, 2);
+ CHECKRV(spirv, "spi_readstatus1");
+ return (spirv == 0) ? (spirxbuf[1]) : -1;
+}
+
+int spi_waitnotbusy(const char *msg) {
+ int spirv = 0;
+ int i = HAL_GetTick();
+ u8 spitxbuf[1] = { 5 };
+ spi_setcs();
+ spirv = HAL_SPI_TransmitReceive(&hspi2, (uint8_t*) spitxbuf, (uint8_t*) spirxbuf, 1, -1);
+ CHECKRV(spirv, "spi_waitnotbusy1");
+ spirxbuf[0] = 0xff;
+ while (spirxbuf[0] & 1) {
+ spirxbuf[0] = 0;
+ spirv = HAL_SPI_TransmitReceive(&hspi2, (uint8_t*) spitxbuf, (uint8_t*) spirxbuf, 1, -1);
+ CHECKRV(spirv, "spi_waitnotbusy2");
+ if (spirv)
+ break;
+ }
+ spi_clearcs();
+ int t = HAL_GetTick() - i;
+ if (t > 10)
+ DebugLog("flash write/erase operation [%s] took %dms\r\n", msg, t);
+ return spirv;
+}
+
+int spi_read256(u32 addr, u8 *dst) {
+ spi_setchip(addr);
+ spibigtx[0] = 3;
+ spibigtx[1] = addr >> 16;
+ spibigtx[2] = addr >> 8;
+ spibigtx[3] = addr >> 0;
+ int spirv = spi_command(spibigtx, 4 + 256);
+ CHECKRV(spirv, "spi_read256");
+ memcpy(dst, spirxbuf + 4, 256);
+ return spirv;
+}
+int spi_read4k(u32 addr, u8 *dst) {
+ for (int i = 0; i < 4096; i += 256, addr += 256, dst += 256) {
+ int spirv = spi_read256(addr, dst);
+ if (spirv)
+ return spirv;
+ }
+ return 0;
+}
+int spi_write4k(u32 addr, u8 *src) {
+ spi_setchip(addr);
+ spi_writeenable();
+ spibigtx[0] = 0x20;
+ spibigtx[1] = addr >> 16;
+ spibigtx[2] = addr >> 8;
+ spibigtx[3] = addr >> 0;
+ int spirv = spi_command(spibigtx, 4);
+ CHECKRV(spirv, "spi_erase");
+ if (spirv != 0)
+ return spirv;
+ spi_waitnotbusy("erase");
+ for (int p = 0; p < 4096; p += 256, addr += 256, src += 256) {
+ spi_writeenable();
+ spibigtx[0] = 2;
+ spibigtx[1] = addr >> 16;
+ spibigtx[2] = addr >> 8;
+ spibigtx[3] = addr >> 0;
+ memcpy(spibigtx + 4, src, 256);
+ spirv = spi_command(spibigtx, 4 + 256);
+ CHECKRV(spirv, "spi_write");
+ if (spirv == 0)
+ spirv = spi_waitnotbusy("write");
+ if (spirv)
+ break;
+ }
+ return spirv;
+}
+
+void flash_program_array(void *addr, void *srcbuf, int size_bytes) {
+ FLASH_EraseInitTypeDef EraseInitStruct;
+ int page = (((size_t) addr) & 0xffffff) / 2048;
+ if (page < 32 || page >= 512) // protect bootloader!
+ return;
+ EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
+ EraseInitStruct.Banks = (page >= 256) ? FLASH_BANK_2 : FLASH_BANK_1;
+ EraseInitStruct.Page = /*page*/page & 255;
+ EraseInitStruct.NbPages = (size_bytes + 2047) / 2048;
+ uint32_t SECTORError = 0;
+ if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) != HAL_OK) {
+ DebugLog("flash %d erase error %d\r\n", page, SECTORError);
+ return;
+ } else {
+ DebugLog("flash %d erased ok!\r\n", page);
+ }
+ __HAL_FLASH_DATA_CACHE_DISABLE();
+ __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
+ __HAL_FLASH_DATA_CACHE_RESET();
+ __HAL_FLASH_INSTRUCTION_CACHE_RESET();
+ __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
+ __HAL_FLASH_DATA_CACHE_ENABLE();
+ uint64_t *s = (uint64_t*) srcbuf;
+ volatile uint64_t *d = (volatile uint64_t*) addr;
+ for (; size_bytes > 0; size_bytes -= 8) {
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t) (size_t) (d++), *s++);
+ }
+}
+
+void flushFlash(void) {
+ lastFlush = ms;
+ if (flashAddr == NO_CACHE)
+ return;
+ DBG("Flush at %x", flashAddr);
+ if ((size_t) flashAddr & 0x40000000) {
+ DBG("Write SPI flush at %x", flashAddr);
+ spi_write4k((size_t) flashAddr, flashBuf);
+ } else if (flashAddr >= 0x08010000 && flashAddr < 0x08100000) {
+ if (memcmp(flashBuf, (void*) flashAddr, FLASH_PAGE_SIZE) != 0) {
+ DBG("MCU Write flush at %x", flashAddr);
+ target_flash_unlock();
+ flash_program_array((void*) flashAddr, (void*) flashBuf,
+ FLASH_PAGE_SIZE);
+ target_flash_lock();
+ }
+ } else if (flashAddr >= DELAY_BUF && flashAddr <= DELAY_BUF + 65536) {
+ memcpy((void*) flashAddr, flashBuf, FLASH_PAGE_SIZE);
+ u32 sector = (flashAddr & 65535) / FLASH_PAGE_SIZE;
+ ((char*) REVERB_BUF)[sector] = 1; // mark this sector as having been updated
+ ((uint32_t*) REVERB_BUF)[64] = 0xa738ea75; // leave a magic number there
+
+ DBG("bootloader flush at %x sector %d", flashAddr, sector);
+ }
+ flashAddr = NO_CACHE;
+}
+
+int clustersize(int bytes) {
+ return (bytes+2043)/2048;
+}
+
+void flash_write(uint32_t dst, const uint8_t *src, int len) {
+ uint32_t newAddr = dst & ~(FLASH_PAGE_SIZE - 1);
+ if (newAddr != flashAddr) {
+ flushFlash();
+ flashAddr = newAddr;
+ if ((size_t) flashAddr & 0x40000000)
+ spi_read4k((size_t) flashAddr, flashBuf);
+ else
+ memcpy(flashBuf, (void*) newAddr, FLASH_PAGE_SIZE);
+ }
+ memcpy(flashBuf + (dst & (FLASH_PAGE_SIZE - 1)), src, len);
+}
+#else
+void flushFlash(void) {}
+#endif
+
+// called roughly every 1ms
+void ghostfat_1ms() {
+ ms++;
+#ifdef FLASH_PAGE_SIZE
+ if (lastFlush && ms - lastFlush > 100) {
+ flushFlash();
+ }
+#endif
+}
+
+static void padded_memcpy(char *dst, const char *src, int len) {
+ for (int i = 0; i < len; ++i) {
+ if (*src)
+ *dst = *src++;
+ else
+ *dst = ' ';
+ dst++;
+ }
+}
+
+int read_block(uint32_t block_no, uint8_t *data) {
+ memset(data, 0, 512);
+ uint32_t sectionIdx = block_no;
+
+ if (block_no == 0) {
+ memcpy(data, &BootBlock, sizeof(BootBlock));
+ data[510] = 0x55;
+ data[511] = 0xaa;
+ } else if (block_no < START_ROOTDIR) {
+ sectionIdx -= START_FAT0;
+ if (sectionIdx >= SECTORS_PER_FAT)
+ sectionIdx -= SECTORS_PER_FAT;
+ if (sectionIdx == 0) {
+ data[0] = 0xf0;
+ data[1] = 0xff;
+ }
+ int basecluster = sectionIdx * 256;
+ int first_cluster=2;
+ for (int f = 0; f < NUM_FILES; ++f) {
+ int c0 = first_cluster - basecluster;
+ int num_clusters=clustersize(info[f].size);
+ first_cluster+=num_clusters;
+ int c1 = c0 + num_clusters;
+ int last = c1 - 1;
+ if (c0 < 0)
+ c0 = 0;
+ if (c1 > 256)
+ c1 = 256;
+ for (int i = c0; i < c1; ++i)
+ ((uint16_t*) (void*) data)[i] = (i == last) ? 0xffff : i + basecluster + 1;
+ }
+ } else if (block_no < START_CLUSTERS) {
+ sectionIdx -= START_ROOTDIR;
+ if (sectionIdx == 0) {
+ DirEntry *d = (void*) data;
+ padded_memcpy(d->name, (const char*) BootBlock.VolumeLabel, 11);
+ d->attrs = 0x28;
+ int first_cluster=2;
+ for (int i = 0; i < NUM_FILES; ++i) {
+ d++;
+ const struct UF2File *inf = &info[i];
+ d->size = inf->size;
+ d->startCluster = first_cluster;
+ first_cluster+=clustersize(d->size);
+ padded_memcpy(d->name, inf->name, 11);
+ }
+ }
+ } else {
+ sectionIdx -= START_CLUSTERS;
+ int first_cluster=2;
+ for (int f = 0; f < NUM_FILES; ++f) {
+ int sector_in_file = sectionIdx - (first_cluster - 2) * 4;
+ first_cluster+=clustersize(info[f].size);
+ if (sector_in_file < 0)
+ continue;
+ if (f < FIRST_UF2_FILE) { // non-uf2 file - return data raw
+ int sizeleft = info[f].size - sector_in_file * 512;
+ if (sizeleft <= 0)
+ continue;
+ if (sizeleft > 512)
+ sizeleft = 512;
+ memcpy(data, info[f].content + sector_in_file * 512, sizeleft);
+ } else { // uf2 file - a 512 byte sector only serves 256 bytes of payload
+ if (sector_in_file >= info[f].size / 512)
+ continue;
+ const char *addr = info[f].content + (sector_in_file * 256);
+ UF2_Block *bl = (void*) data;
+ bl->magicStart0 = UF2_MAGIC_START0;
+ bl->magicStart1 = UF2_MAGIC_START1;
+ bl->magicEnd = UF2_MAGIC_END;
+ bl->blockNo = sector_in_file;
+ bl->numBlocks = info[f].size / 512;
+ bl->targetAddr = (size_t) addr;
+ bl->payloadSize = 256;
+
+ if ((size_t)addr & 0x40000000) {
+ // read spi
+ spi_read256((size_t) addr, bl->data);
+ } else {
+ if ((size_t)addr>=0x08000000 && (size_t)addr<0x08080000) { // code region
+ bl->flags |= UF2_FLAG_FAMILYID_PRESENT; // family ID present
+ bl->familyID = UF2_FAMILY; // STM32L4xx
+ }
+ memcpy(bl->data, (void*) addr, bl->payloadSize);
+ }
+ }
+
+ break;
+ }
+ }
+ return 0;
+}
+
+int write_block(uint32_t lba, const uint8_t *copy_from) {
+ const UF2_Block *bl = (const void*) copy_from;
+ if (!is_uf2_block(bl) || !UF2_IS_MY_FAMILY(bl)) {
+ return 0;
+ }
+ if ((bl->flags & UF2_FLAG_NOFLASH) || bl->payloadSize > 256 || (bl->targetAddr & 0xff)) {
+ DBG("Skip block at %x", bl->targetAddr);
+ return 0;
+ }
+ for (int f = FIRST_UF2_FILE; f < NUM_FILES; ++f) {
+ int sector_in_file = (bl->targetAddr - (size_t) info[f].content) / 256;
+ //if (sector_in_file!=bl->blockNo)
+ // continue;
+ if (sector_in_file < 0 || sector_in_file >= info[f].size / 512)
+ continue;
+ flash_write(bl->targetAddr, bl->data, bl->payloadSize);
+ break;
+ }
+ return 0;
+}
+