From c51a227fd9a85e85e925d31a8f525e67d865ee0a Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 13 Mar 2019 15:20:42 -0700 Subject: [PATCH 1/9] Add static assert This guards against future increase in number of files causing buffer overflow when reading the first root directory sector. --- src/usb/uf2/ghostfat.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 1f41c10..7262bfc 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -97,6 +97,11 @@ static struct TextFile const info[] = { #define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT) #define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS) +// all directory entries must fit in a single sector +// because otherwise current code overflows buffer +STATIC_ASSERT(NUM_INFO < (512 / sizeof(DirEntry))); + + static FAT_BootBlock const BootBlock = { .JumpInstruction = {0xeb, 0x3c, 0x90}, .OEMInfo = "UF2 UF2 ", @@ -198,6 +203,7 @@ void read_block(uint32_t block_no, uint8_t *data) { ((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1; } } else if (block_no < START_CLUSTERS) { + // Use STATIC_ASSERT() above to ensure only first sector has entries sectionIdx -= START_ROOTDIR; if (sectionIdx == 0) { DirEntry *d = (void *)data; From 8def8aee53003dd4e184216c3d8233e2ca5f9af1 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 13 Mar 2019 18:27:11 -0700 Subject: [PATCH 2/9] Safer code Add additional compile-time assertions Add additional comments to define limitations future maintainers should be aware of. --- src/usb/uf2/ghostfat.c | 28 +++++--- src/usb/uf2/uf2.h | 144 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 8 deletions(-) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 7262bfc..674ba1a 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -9,6 +9,7 @@ #include "bootloader_settings.h" #include "bootloader.h" + typedef struct { uint8_t JumpInstruction[3]; uint8_t OEMInfo[8]; @@ -59,6 +60,7 @@ struct TextFile { #define STR0(x) #x #define STR(x) STR0(x) + const char infoUf2File[] = // "UF2 Bootloader " UF2_VERSION "\r\n" "Model: " PRODUCT_NAME "\r\n" @@ -81,11 +83,20 @@ static struct TextFile const info[] = { {.name = "INDEX HTM", .content = indexFile}, {.name = "CURRENT UF2"}, }; -#define NUM_INFO (sizeof(info) / sizeof(info[0])) + +// WARNING -- code presumes each non-UF2 file content fits in single sector +// Cannot programmatically statically assert .content length +// for each element above. +STATIC_ASSERT(ARRAY_SIZE2(indexFile) < 512); + + +#define NUM_FILES (ARRAY_SIZE2(info)) +#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry + #define UF2_SIZE (current_flash_size() * 2) #define UF2_SECTORS (UF2_SIZE / 512) -#define UF2_FIRST_SECTOR (NUM_INFO + 1) +#define UF2_FIRST_SECTOR (NUM_FILES + 1) // WARNING -- code presumes each non-UF2 file content fits in single sector #define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1) #define RESERVED_SECTORS 1 @@ -99,7 +110,8 @@ static struct TextFile const info[] = { // all directory entries must fit in a single sector // because otherwise current code overflows buffer -STATIC_ASSERT(NUM_INFO < (512 / sizeof(DirEntry))); +STATIC_ASSERT(NUM_DIRENTRIES < (512 / sizeof(DirEntry))); +// STATIC_ASSERT(NUM_DIRENTRIES < (512 / sizeof(DirEntry)) * ROOT_DIR_SECTORS); static FAT_BootBlock const BootBlock = { @@ -193,8 +205,8 @@ void read_block(uint32_t block_no, uint8_t *data) { sectionIdx -= SECTORS_PER_FAT; if (sectionIdx == 0) { data[0] = 0xf0; - for (int i = 1; i < NUM_INFO * 2 + 4; ++i) { - data[i] = 0xff; + for (int i = 1; i < NUM_FILES * 2 + 4; ++i) { + data[i] = 0xff; // WARNING -- code presumes each non-UF2 file content fits in single sector } } for (int i = 0; i < 256; ++i) { @@ -209,7 +221,7 @@ void read_block(uint32_t block_no, uint8_t *data) { DirEntry *d = (void *)data; padded_memcpy(d->name, (char const *) BootBlock.VolumeLabel, 11); d->attrs = 0x28; - for (int i = 0; i < NUM_INFO; ++i) { + for (int i = 0; i < NUM_FILES; ++i) { d++; struct TextFile const *inf = &info[i]; d->size = inf->content ? strlen(inf->content) : UF2_SIZE; @@ -219,10 +231,10 @@ void read_block(uint32_t block_no, uint8_t *data) { } } else { sectionIdx -= START_CLUSTERS; - if (sectionIdx < NUM_INFO - 1) { + if (sectionIdx < NUM_FILES - 1) { memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content)); } else { - sectionIdx -= NUM_INFO - 1; + sectionIdx -= NUM_FILES - 1; uint32_t addr = USER_FLASH_START + sectionIdx * 256; if (addr < USER_FLASH_START+FLASH_SIZE) { UF2_Block *bl = (void *)data; diff --git a/src/usb/uf2/uf2.h b/src/usb/uf2/uf2.h index 6edbb4f..8d82d28 100644 --- a/src/usb/uf2/uf2.h +++ b/src/usb/uf2/uf2.h @@ -152,3 +152,147 @@ static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining #endif #endif + +#ifndef ARRAYSIZE2_H +#define ARRAYSIZE2_H + +#ifndef __has_feature + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#if __cplusplus >= 199711L + #pragma message "using Ivan J. Johnson's ARRAY_SIZE2" + + // Works on older compilers, even Visual C++ 6.... + // Created by Ivan J. Johnson, March 06, 2007 + // See http://drdobbs.com/cpp/197800525?pgno=1 + // + // Pseudocode: + // if x is not an array + // issue a compile-time error + // else + // use the traditional (non-typesafe) C99 COUNTOF expression + // + // If the argument is any of: + // object of class type, such as an std::vector + // floating-point type + // function pointer + // pointer-to-member + // then the first reinterpret_cast<> is not legal (compiler error) + // + // The type for check1 is chosen and named to help understand + // the cause of the error, because the class name is likely to + // appear in the compiler error message. + // + // If check1 succeeds, then the argument must be one of: + // an integral type + // an enumerated type + // a pointer to an object + // an array + // + // Check2 expands approximately to sizeof(check_type(x, &x)), + // where check_type is an overloaded function. + // Because this is purely a compile-time computation, + // the function is never really called or even implemented, + // but it lets the compiler apply overload resolution, + // which allows further type discrimination. + // There are three possibilities to consider: + // x is an integral type or enumerated type. + // In this case, neither of the two function overloads + // is a match, resulting in a compiler error. + // x is a pointer to an object. + // In this case, the first argument to check_type() + // is a pointer and the second one is a pointer-to-pointer. + // The best function match is the first overload of check_type, + // the one that returns an incomplete type (Is_pointer). + // However, because Is_pointer is an incomplete type, + // sizeof(Is_pointer) is not a valid expression, + // resulting in a compiler error. + // x is an array. + // In this case, the first argument to check_type() + // is an array and the second is a pointer-to-array. + // A pointer-to-array is *NOT* convertible to a + // pointer-to-pointer, so the first overload of + // check_type() is not a match. + // However, an array IS convertible to a pointer, + // and a pointer-to-array already is a pointer. + // Any pointer is convertible to a void*, + // so the second function overload is a match. + // That overload returns a complete type (Is_array). + // Because it's a complete type, + // sizeof(Is_array) is a valid expression. + // Thus, the compiler has EXCLUDED every possible type + // except arrays via compilation errors before reaching + // the third line. + // Moreover, check1 and check2 are reduced to the value zero, + // while the third line is the old type-unsafe C-style macro, + // now made entirely type-safe. + // + // Additional benefits: + // The result is itself constexpr + // + // + #define ARRAY_SIZE2(arr) ( \ + 0 * sizeof(reinterpret_cast(arr)) + /*check1*/ \ + 0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + /*check2*/ \ + sizeof(arr) / sizeof((arr)[0]) /* eval */ \ + ) + + struct Bad_arg_to_COUNTOF { + class Is_pointer; // incomplete + class Is_array {}; + template + static Is_pointer check_type(const T*, const T* const*); + static Is_array check_type(const void*, const void*); + }; + +#elif __cplusplus >= 201103L || /* any compiler claiming C++11 support */ \ + _MSC_VER >= 1900 || /* Visual C++ 2015 or higher */ \ + __has_feature(cxx_constexpr) /* CLang versions supporting constexp */ + + #pragma message "C++11 version ARRAY_SIZE2" + + namespace detail + { + template + constexpr std::size_t countof(T const (&)[N]) noexcept + { + return N; + } + } // namespace detail + #define ARRAY_SIZE2(arr) detail::countof(arr) + +#elif _MSC_VER // Visual C++ fallback + + #pragma message "using Microsoft Visual C++ intrinsic ARRAY_SIZE2" + #define ARRAY_SIZE2(arr) _countof(arr) + +#elif __cplusplus >= 199711L && ( /* C++ 98 trick */ \ + defined(__INTEL_COMPILER) || \ + defined(__clang__) || \ + (defined(__GNUC__) && ( \ + (__GNUC__ > 4) || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \ + ))) + + #pragma message "C++98 version ARRAY_SIZE2" + + template + char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N]; + #define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x)) + +#else + + #pragma message "Using type-unsafe version of ARRAY_SIZE2" + // This is the worst-case scenario macro. + // While it is valid C, it is NOT typesafe. + // For example, if the parameter arr is a pointer instead of array, + // the compiler will SILENTLY give a (likely) incorrect result. + #define ARRAY_SIZE2(arr) sizeof(arr) / sizeof(arr[0]) + +#endif + + +#endif // ARRAYSIZE2_H + + From 53a99195bfc3aa549268f95bb0ecef401cf65c77 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 13 Mar 2019 18:45:44 -0700 Subject: [PATCH 3/9] Raises file limit from 14 to 62 files. Enable all four sectors of root directory to contain files. --- src/usb/uf2/ghostfat.c | 95 +++++++++++++++++++++++++++++++++++------- src/usb/uf2/uf2.h | 35 ++++++++++++++++ 2 files changed, 115 insertions(+), 15 deletions(-) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 674ba1a..b3b3cbe 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -9,6 +9,8 @@ #include "bootloader_settings.h" #include "bootloader.h" +// #define CREATE_MANY_FILES + typedef struct { uint8_t JumpInstruction[3]; @@ -48,7 +50,6 @@ typedef struct { uint16_t startCluster; uint32_t size; } __attribute__((packed)) DirEntry; - STATIC_ASSERT(sizeof(DirEntry) == 32); struct TextFile { @@ -78,9 +79,56 @@ const char indexFile[] = // "" "\n"; +#ifdef CREATE_MANY_FILES +const char dataFile00[] = "This is the data for File 00\r\n"; +const char dataFile01[] = "This is the data for File 01\r\n"; +const char dataFile02[] = "This is the data for File 02\r\n"; +const char dataFile03[] = "This is the data for File 03\r\n"; +const char dataFile04[] = "This is the data for File 04\r\n"; +const char dataFile05[] = "This is the data for File 05\r\n"; +const char dataFile06[] = "This is the data for File 06\r\n"; +const char dataFile07[] = "This is the data for File 07\r\n"; +const char dataFile08[] = "This is the data for File 08\r\n"; +const char dataFile09[] = "This is the data for File 09\r\n"; +const char dataFile10[] = "This is the data for File 10\r\n"; +const char dataFile11[] = "This is the data for File 11\r\n"; +const char dataFile12[] = "This is the data for File 12\r\n"; +const char dataFile13[] = "This is the data for File 13\r\n"; +const char dataFile14[] = "This is the data for File 14\r\n"; +const char dataFile15[] = "This is the data for File 15\r\n"; +const char dataFile16[] = "This is the data for File 16\r\n"; +const char dataFile17[] = "This is the data for File 17\r\n"; +const char dataFile18[] = "This is the data for File 18\r\n"; +const char dataFile19[] = "This is the data for File 19\r\n"; +#endif // CREATE_MANY_FILES + +// WARNING -- code presumes only one NULL .content for .UF2 file +// and requires it be the last element of the array static struct TextFile const info[] = { {.name = "INFO_UF2TXT", .content = infoUf2File}, {.name = "INDEX HTM", .content = indexFile}, +#ifdef CREATE_MANY_FILES + {.name = "FILE00 TXT", .content = dataFile00}, + {.name = "FILE01 TXT", .content = dataFile01}, + {.name = "FILE02 TXT", .content = dataFile02}, + {.name = "FILE03 TXT", .content = dataFile03}, + {.name = "FILE04 TXT", .content = dataFile04}, + {.name = "FILE05 TXT", .content = dataFile05}, + {.name = "FILE06 TXT", .content = dataFile06}, + {.name = "FILE07 TXT", .content = dataFile07}, + {.name = "FILE08 TXT", .content = dataFile08}, + {.name = "FILE09 TXT", .content = dataFile09}, + {.name = "FILE10 TXT", .content = dataFile10}, + {.name = "FILE11 TXT", .content = dataFile11}, + {.name = "FILE12 TXT", .content = dataFile12}, + {.name = "FILE13 TXT", .content = dataFile13}, + {.name = "FILE14 TXT", .content = dataFile14}, + {.name = "FILE15 TXT", .content = dataFile15}, + {.name = "FILE16 TXT", .content = dataFile16}, + {.name = "FILE17 TXT", .content = dataFile17}, + {.name = "FILE18 TXT", .content = dataFile18}, + {.name = "FILE19 TXT", .content = dataFile19}, +#endif {.name = "CURRENT UF2"}, }; @@ -110,8 +158,9 @@ STATIC_ASSERT(ARRAY_SIZE2(indexFile) < 512); // all directory entries must fit in a single sector // because otherwise current code overflows buffer -STATIC_ASSERT(NUM_DIRENTRIES < (512 / sizeof(DirEntry))); -// STATIC_ASSERT(NUM_DIRENTRIES < (512 / sizeof(DirEntry)) * ROOT_DIR_SECTORS); +#define DIRENTRIES_PER_SECTOR (512/sizeof(DirEntry)) + +STATIC_ASSERT(NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTORS); static FAT_BootBlock const BootBlock = { @@ -121,7 +170,7 @@ static FAT_BootBlock const BootBlock = { .SectorsPerCluster = 1, .ReservedSectors = RESERVED_SECTORS, .FATCopies = 2, - .RootDirectoryEntries = (ROOT_DIR_SECTORS * 512 / 32), + .RootDirectoryEntries = (ROOT_DIR_SECTORS * DIRENTRIES_PER_SECTOR), .TotalSectors16 = NUM_FAT_BLOCKS - 2, .MediaDescriptor = 0xF8, .SectorsPerFAT = SECTORS_PER_FAT, @@ -205,8 +254,11 @@ void read_block(uint32_t block_no, uint8_t *data) { sectionIdx -= SECTORS_PER_FAT; if (sectionIdx == 0) { data[0] = 0xf0; + // WARNING -- code presumes only one NULL .content for .UF2 file + // and all non-NULL .content fit in one sector + // and requires it be the last element of the array for (int i = 1; i < NUM_FILES * 2 + 4; ++i) { - data[i] = 0xff; // WARNING -- code presumes each non-UF2 file content fits in single sector + data[i] = 0xff; } } for (int i = 0; i < 256; ++i) { @@ -215,20 +267,33 @@ void read_block(uint32_t block_no, uint8_t *data) { ((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1; } } else if (block_no < START_CLUSTERS) { - // Use STATIC_ASSERT() above to ensure only first sector has entries + sectionIdx -= START_ROOTDIR; - if (sectionIdx == 0) { - DirEntry *d = (void *)data; + + DirEntry *d = (void *)data; + int remainingEntries = DIRENTRIES_PER_SECTOR; + if (sectionIdx == 0) { // volume label first + // volume label is first directory entry padded_memcpy(d->name, (char const *) BootBlock.VolumeLabel, 11); d->attrs = 0x28; - for (int i = 0; i < NUM_FILES; ++i) { - d++; - struct TextFile const *inf = &info[i]; - d->size = inf->content ? strlen(inf->content) : UF2_SIZE; - d->startCluster = i + 2; - padded_memcpy(d->name, inf->name, 11); - } + remainingEntries--; } + + for (int i = DIRENTRIES_PER_SECTOR * sectionIdx; + remainingEntries > 0 && i < NUM_FILES; + i++, d++) { + struct TextFile const * inf = &info[i]; + // WARNING -- code presumes only one NULL .content for .UF2 file + // and requires it be the last element of the array + d->size = inf->content ? strlen(inf->content) : UF2_SIZE; + d->startCluster = i + 2; + padded_memcpy(d->name, inf->name, 11); + // DIR_WrtTime and DIR_WrtDate must be supported + d->updateDate = __DOSDATE__; + d->lastAccessDate = __DOSDATE__; + d->createDate = __DOSDATE__; + } + } else { sectionIdx -= START_CLUSTERS; if (sectionIdx < NUM_FILES - 1) { diff --git a/src/usb/uf2/uf2.h b/src/usb/uf2/uf2.h index 8d82d28..eee26d6 100644 --- a/src/usb/uf2/uf2.h +++ b/src/usb/uf2/uf2.h @@ -296,3 +296,38 @@ static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining #endif // ARRAYSIZE2_H +#ifndef COMPILE_DATE_H +#define COMPILE_DATE_H + +#define __YEAR_INT__ ((( \ + (__DATE__ [ 7u] - '0') * 10u + \ + (__DATE__ [ 8u] - '0')) * 10u + \ + (__DATE__ [ 9u] - '0')) * 10u + \ + (__DATE__ [10u] - '0')) + +#define __MONTH_INT__ ( \ + (__DATE__ [2u] == 'n' && __DATE__ [1u] == 'a') ? 1u /*Jan*/ \ +: (__DATE__ [2u] == 'b' ) ? 2u /*Feb*/ \ +: (__DATE__ [2u] == 'r' && __DATE__ [1u] == 'a') ? 3u /*Mar*/ \ +: (__DATE__ [2u] == 'r' ) ? 4u /*Apr*/ \ +: (__DATE__ [2u] == 'y' ) ? 5u /*May*/ \ +: (__DATE__ [2u] == 'n' ) ? 6u /*Jun*/ \ +: (__DATE__ [2u] == 'l' ) ? 7u /*Jul*/ \ +: (__DATE__ [2u] == 'g' ) ? 8u /*Jul*/ \ +: (__DATE__ [2u] == 'p' ) ? 9u /*Jul*/ \ +: (__DATE__ [2u] == 't' ) ? 10u /*Jul*/ \ +: (__DATE__ [2u] == 'v' ) ? 11u /*Jul*/ \ +: 12u /*Dec*/ ) + +#define __DAY_INT__ ( \ + (__DATE__ [4u] == ' ' ? 0u : __DATE__ [4u] - '0') * 10u \ + + (__DATE__ [5u] - '0') ) + +#define __DOSDATE__ ( \ + ((__YEAR_INT__ - 1980u) << 9u) | \ + ( __MONTH_INT__ << 5u) | \ + ( __DAY_INT__ << 0u) ) + + +#endif // COMPILE_DATE_H + From 174e6e9724a1a7f5c28e4515c381c221170dfee3 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 13 Mar 2019 22:05:57 -0700 Subject: [PATCH 4/9] Fix Directory entries The media would show as corrupt in CMD.EXE in Windows. This is because the date/time fields were invalid. Since this needed to be fixed anyways, update so that the date and time correspond to the time that the preprocessor parsed the source file. --- src/usb/uf2/ghostfat.c | 26 ++++++++++++++++++-------- src/usb/uf2/uf2.h | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index b3b3cbe..9143cde 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -9,9 +9,9 @@ #include "bootloader_settings.h" #include "bootloader.h" +// Uncomment the next line to create an additional 20 files in root. // #define CREATE_MANY_FILES - typedef struct { uint8_t JumpInstruction[3]; uint8_t OEMInfo[8]; @@ -67,7 +67,8 @@ const char infoUf2File[] = // "Model: " PRODUCT_NAME "\r\n" "Board-ID: " BOARD_ID "\r\n" "Bootloader: " BOOTLOADER_ID "\r\n" - "Date: " __DATE__ "\r\n"; + "Date: " __DATE__ "\r\n" + "Time: " __TIME__ "\r\n"; const char indexFile[] = // "\n" @@ -276,22 +277,31 @@ void read_block(uint32_t block_no, uint8_t *data) { // volume label is first directory entry padded_memcpy(d->name, (char const *) BootBlock.VolumeLabel, 11); d->attrs = 0x28; + d++; remainingEntries--; } for (int i = DIRENTRIES_PER_SECTOR * sectionIdx; remainingEntries > 0 && i < NUM_FILES; i++, d++) { + + // WARNING -- code presumes all but last file take exactly one sector + uint16_t startCluster = i + 2; + struct TextFile const * inf = &info[i]; + padded_memcpy(d->name, inf->name, 11); + d->createTimeFine = __SECONDS_INT__ % 2 * 100; + d->createTime = __DOSTIME__; + d->createDate = __DOSDATE__; + d->lastAccessDate = __DOSDATE__; + d->highStartCluster = startCluster >> 8; + // DIR_WrtTime and DIR_WrtDate must be supported + d->updateTime = __DOSTIME__; + d->updateDate = __DOSDATE__; + d->startCluster = startCluster & 0xFF; // WARNING -- code presumes only one NULL .content for .UF2 file // and requires it be the last element of the array d->size = inf->content ? strlen(inf->content) : UF2_SIZE; - d->startCluster = i + 2; - padded_memcpy(d->name, inf->name, 11); - // DIR_WrtTime and DIR_WrtDate must be supported - d->updateDate = __DOSDATE__; - d->lastAccessDate = __DOSDATE__; - d->createDate = __DOSDATE__; } } else { diff --git a/src/usb/uf2/uf2.h b/src/usb/uf2/uf2.h index eee26d6..6116f3f 100644 --- a/src/usb/uf2/uf2.h +++ b/src/usb/uf2/uf2.h @@ -323,11 +323,30 @@ static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining (__DATE__ [4u] == ' ' ? 0u : __DATE__ [4u] - '0') * 10u \ + (__DATE__ [5u] - '0') ) +// __TIME__ expands to an eight-character string constant +// "23:59:01", or (if cannot determine time) "??:??:??" +#define __HOUR_INT__ ( \ + (__TIME__ [0u] == '?' ? 0u : __TIME__ [0u] - '0') * 10u \ + + (__TIME__ [1u] == '?' ? 0u : __TIME__ [1u] - '0') ) + +#define __MINUTE_INT__ ( \ + (__TIME__ [3u] == '?' ? 0u : __TIME__ [3u] - '0') * 10u \ + + (__TIME__ [4u] == '?' ? 0u : __TIME__ [4u] - '0') ) + +#define __SECONDS_INT__ ( \ + (__TIME__ [6u] == '?' ? 0u : __TIME__ [6u] - '0') * 10u \ + + (__TIME__ [7u] == '?' ? 0u : __TIME__ [7u] - '0') ) + + #define __DOSDATE__ ( \ ((__YEAR_INT__ - 1980u) << 9u) | \ ( __MONTH_INT__ << 5u) | \ ( __DAY_INT__ << 0u) ) +#define __DOSTIME__ ( \ + ( __HOUR_INT__ << 11u) | \ + ( __MONTH_INT__ << 5u) | \ + ( __DAY_INT__ << 0u) ) #endif // COMPILE_DATE_H From 04e0df1c1868f8c703469cbfa782083bccb59efb Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 13 Mar 2019 23:40:18 -0700 Subject: [PATCH 5/9] prepare to split file --- src/usb/uf2/macros.h | 352 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 src/usb/uf2/macros.h diff --git a/src/usb/uf2/macros.h b/src/usb/uf2/macros.h new file mode 100644 index 0000000..6116f3f --- /dev/null +++ b/src/usb/uf2/macros.h @@ -0,0 +1,352 @@ +/** + +Microsoft UF2 + +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +All rights reserved. + +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. + +*/ +#ifndef UF2FORMAT_H +#define UF2FORMAT_H 1 + +#include "uf2cfg.h" + +#include +#include + +#include "app_util.h" + +#include "dfu_types.h" + +#define SD_MAGIC_NUMBER 0x51b1e5db +#define SD_MAGIC_OK() (*((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == 0x51b1e5db) +extern bool sdRunning; + +// All entries are little endian. + +#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" +#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected +#define UF2_MAGIC_END 0x0AB16F30UL // Ditto + +// If set, the block is "comment" and should not be flashed to the device +#define UF2_FLAG_NOFLASH 0x00000001 +#define UF2_FLAG_FAMILYID 0x00002000 + +#define MAX_BLOCKS (FLASH_SIZE / 256 + 100) +typedef struct { + uint32_t numBlocks; + uint32_t numWritten; + uint8_t writtenMask[MAX_BLOCKS / 8 + 1]; +} WriteState; + +typedef struct { + // 32 byte header + uint32_t magicStart0; + uint32_t magicStart1; + uint32_t flags; + uint32_t targetAddr; + uint32_t payloadSize; + uint32_t blockNo; + uint32_t numBlocks; + uint32_t familyID; + + // raw data; + uint8_t data[476]; + + // store magic also at the end to limit damage from partial block reads + uint32_t magicEnd; +} UF2_Block; + +typedef struct { + uint8_t version; + uint8_t ep_in; + uint8_t ep_out; + uint8_t reserved0; + uint32_t cbw_tag; + uint32_t blocks_remaining; + uint8_t *buffer; +} UF2_HandoverArgs; + +typedef void (*UF2_MSC_Handover_Handler)(UF2_HandoverArgs *handover); +typedef void (*UF2_HID_Handover_Handler)(int ep); + +// this is required to be exactly 16 bytes long by the linker script +typedef struct { + void *reserved0; + UF2_HID_Handover_Handler handoverHID; + UF2_MSC_Handover_Handler handoverMSC; + const char *info_uf2; +} UF2_BInfo; + +#define UF2_BINFO ((UF2_BInfo *)(APP_START_ADDRESS - sizeof(UF2_BInfo))) + +static inline bool is_uf2_block(void *data) { + UF2_Block *bl = (UF2_Block *)data; + return bl->magicStart0 == UF2_MAGIC_START0 && bl->magicStart1 == UF2_MAGIC_START1 && + bl->magicEnd == UF2_MAGIC_END; +} + +static inline bool in_uf2_bootloader_space(const void *addr) { + return USER_FLASH_END <= (uint32_t)addr && (uint32_t)addr < FLASH_SIZE; +} + + +#ifdef UF2_DEFINE_HANDOVER +static inline const char *uf2_info(void) { + if (in_uf2_bootloader_space(UF2_BINFO->info_uf2)) + return UF2_BINFO->info_uf2; + return "N/A"; +} + +static inline void hf2_handover(uint8_t ep) { + const char *board_info = UF2_BINFO->info_uf2; + UF2_HID_Handover_Handler fn = UF2_BINFO->handoverHID; + + if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) && + ((uint32_t)fn & 1)) { + // Pass control to bootloader; never returns + fn(ep & 0xf); + } +} + +// the ep_in/ep_out are without the 0x80 mask +// cbw_tag is in the same bit format as it came +static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining, uint8_t ep_in, + uint8_t ep_out, uint32_t cbw_tag) { + if (!is_uf2_block(buffer)) + return; + + const char *board_info = UF2_BINFO->info_uf2; + UF2_MSC_Handover_Handler fn = UF2_BINFO->handoverMSC; + + if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) && + ((uint32_t)fn & 1)) { + UF2_HandoverArgs hand = { + 1, ep_in, ep_out, 0, cbw_tag, blocks_remaining, buffer, + }; + // Pass control to bootloader; never returns + fn(&hand); + } +} +#endif + +#endif + +#ifndef ARRAYSIZE2_H +#define ARRAYSIZE2_H + +#ifndef __has_feature + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#if __cplusplus >= 199711L + #pragma message "using Ivan J. Johnson's ARRAY_SIZE2" + + // Works on older compilers, even Visual C++ 6.... + // Created by Ivan J. Johnson, March 06, 2007 + // See http://drdobbs.com/cpp/197800525?pgno=1 + // + // Pseudocode: + // if x is not an array + // issue a compile-time error + // else + // use the traditional (non-typesafe) C99 COUNTOF expression + // + // If the argument is any of: + // object of class type, such as an std::vector + // floating-point type + // function pointer + // pointer-to-member + // then the first reinterpret_cast<> is not legal (compiler error) + // + // The type for check1 is chosen and named to help understand + // the cause of the error, because the class name is likely to + // appear in the compiler error message. + // + // If check1 succeeds, then the argument must be one of: + // an integral type + // an enumerated type + // a pointer to an object + // an array + // + // Check2 expands approximately to sizeof(check_type(x, &x)), + // where check_type is an overloaded function. + // Because this is purely a compile-time computation, + // the function is never really called or even implemented, + // but it lets the compiler apply overload resolution, + // which allows further type discrimination. + // There are three possibilities to consider: + // x is an integral type or enumerated type. + // In this case, neither of the two function overloads + // is a match, resulting in a compiler error. + // x is a pointer to an object. + // In this case, the first argument to check_type() + // is a pointer and the second one is a pointer-to-pointer. + // The best function match is the first overload of check_type, + // the one that returns an incomplete type (Is_pointer). + // However, because Is_pointer is an incomplete type, + // sizeof(Is_pointer) is not a valid expression, + // resulting in a compiler error. + // x is an array. + // In this case, the first argument to check_type() + // is an array and the second is a pointer-to-array. + // A pointer-to-array is *NOT* convertible to a + // pointer-to-pointer, so the first overload of + // check_type() is not a match. + // However, an array IS convertible to a pointer, + // and a pointer-to-array already is a pointer. + // Any pointer is convertible to a void*, + // so the second function overload is a match. + // That overload returns a complete type (Is_array). + // Because it's a complete type, + // sizeof(Is_array) is a valid expression. + // Thus, the compiler has EXCLUDED every possible type + // except arrays via compilation errors before reaching + // the third line. + // Moreover, check1 and check2 are reduced to the value zero, + // while the third line is the old type-unsafe C-style macro, + // now made entirely type-safe. + // + // Additional benefits: + // The result is itself constexpr + // + // + #define ARRAY_SIZE2(arr) ( \ + 0 * sizeof(reinterpret_cast(arr)) + /*check1*/ \ + 0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + /*check2*/ \ + sizeof(arr) / sizeof((arr)[0]) /* eval */ \ + ) + + struct Bad_arg_to_COUNTOF { + class Is_pointer; // incomplete + class Is_array {}; + template + static Is_pointer check_type(const T*, const T* const*); + static Is_array check_type(const void*, const void*); + }; + +#elif __cplusplus >= 201103L || /* any compiler claiming C++11 support */ \ + _MSC_VER >= 1900 || /* Visual C++ 2015 or higher */ \ + __has_feature(cxx_constexpr) /* CLang versions supporting constexp */ + + #pragma message "C++11 version ARRAY_SIZE2" + + namespace detail + { + template + constexpr std::size_t countof(T const (&)[N]) noexcept + { + return N; + } + } // namespace detail + #define ARRAY_SIZE2(arr) detail::countof(arr) + +#elif _MSC_VER // Visual C++ fallback + + #pragma message "using Microsoft Visual C++ intrinsic ARRAY_SIZE2" + #define ARRAY_SIZE2(arr) _countof(arr) + +#elif __cplusplus >= 199711L && ( /* C++ 98 trick */ \ + defined(__INTEL_COMPILER) || \ + defined(__clang__) || \ + (defined(__GNUC__) && ( \ + (__GNUC__ > 4) || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \ + ))) + + #pragma message "C++98 version ARRAY_SIZE2" + + template + char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N]; + #define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x)) + +#else + + #pragma message "Using type-unsafe version of ARRAY_SIZE2" + // This is the worst-case scenario macro. + // While it is valid C, it is NOT typesafe. + // For example, if the parameter arr is a pointer instead of array, + // the compiler will SILENTLY give a (likely) incorrect result. + #define ARRAY_SIZE2(arr) sizeof(arr) / sizeof(arr[0]) + +#endif + + +#endif // ARRAYSIZE2_H + + +#ifndef COMPILE_DATE_H +#define COMPILE_DATE_H + +#define __YEAR_INT__ ((( \ + (__DATE__ [ 7u] - '0') * 10u + \ + (__DATE__ [ 8u] - '0')) * 10u + \ + (__DATE__ [ 9u] - '0')) * 10u + \ + (__DATE__ [10u] - '0')) + +#define __MONTH_INT__ ( \ + (__DATE__ [2u] == 'n' && __DATE__ [1u] == 'a') ? 1u /*Jan*/ \ +: (__DATE__ [2u] == 'b' ) ? 2u /*Feb*/ \ +: (__DATE__ [2u] == 'r' && __DATE__ [1u] == 'a') ? 3u /*Mar*/ \ +: (__DATE__ [2u] == 'r' ) ? 4u /*Apr*/ \ +: (__DATE__ [2u] == 'y' ) ? 5u /*May*/ \ +: (__DATE__ [2u] == 'n' ) ? 6u /*Jun*/ \ +: (__DATE__ [2u] == 'l' ) ? 7u /*Jul*/ \ +: (__DATE__ [2u] == 'g' ) ? 8u /*Jul*/ \ +: (__DATE__ [2u] == 'p' ) ? 9u /*Jul*/ \ +: (__DATE__ [2u] == 't' ) ? 10u /*Jul*/ \ +: (__DATE__ [2u] == 'v' ) ? 11u /*Jul*/ \ +: 12u /*Dec*/ ) + +#define __DAY_INT__ ( \ + (__DATE__ [4u] == ' ' ? 0u : __DATE__ [4u] - '0') * 10u \ + + (__DATE__ [5u] - '0') ) + +// __TIME__ expands to an eight-character string constant +// "23:59:01", or (if cannot determine time) "??:??:??" +#define __HOUR_INT__ ( \ + (__TIME__ [0u] == '?' ? 0u : __TIME__ [0u] - '0') * 10u \ + + (__TIME__ [1u] == '?' ? 0u : __TIME__ [1u] - '0') ) + +#define __MINUTE_INT__ ( \ + (__TIME__ [3u] == '?' ? 0u : __TIME__ [3u] - '0') * 10u \ + + (__TIME__ [4u] == '?' ? 0u : __TIME__ [4u] - '0') ) + +#define __SECONDS_INT__ ( \ + (__TIME__ [6u] == '?' ? 0u : __TIME__ [6u] - '0') * 10u \ + + (__TIME__ [7u] == '?' ? 0u : __TIME__ [7u] - '0') ) + + +#define __DOSDATE__ ( \ + ((__YEAR_INT__ - 1980u) << 9u) | \ + ( __MONTH_INT__ << 5u) | \ + ( __DAY_INT__ << 0u) ) + +#define __DOSTIME__ ( \ + ( __HOUR_INT__ << 11u) | \ + ( __MONTH_INT__ << 5u) | \ + ( __DAY_INT__ << 0u) ) + +#endif // COMPILE_DATE_H + From 04fc396fd88262136d188f467edc60dab62fd339 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Wed, 13 Mar 2019 23:49:52 -0700 Subject: [PATCH 6/9] Put macros into new file, rather than uf2.h. --- .../Adafruit_nRF52_Bootloader.emProject | 1 + src/usb/uf2/ghostfat.c | 2 + src/usb/uf2/macros.h | 129 +----------- src/usb/uf2/uf2.h | 198 ------------------ 4 files changed, 4 insertions(+), 326 deletions(-) diff --git a/src/segger/Adafruit_nRF52_Bootloader.emProject b/src/segger/Adafruit_nRF52_Bootloader.emProject index 57303ee..cc72c5b 100644 --- a/src/segger/Adafruit_nRF52_Bootloader.emProject +++ b/src/segger/Adafruit_nRF52_Bootloader.emProject @@ -63,6 +63,7 @@ + diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 0496e1d..01ec685 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -1,4 +1,6 @@ +#include "macros.h" + #include "uf2.h" #include "flash_nrf5x.h" #include diff --git a/src/usb/uf2/macros.h b/src/usb/uf2/macros.h index 6116f3f..bcc27aa 100644 --- a/src/usb/uf2/macros.h +++ b/src/usb/uf2/macros.h @@ -1,10 +1,8 @@ /** -Microsoft UF2 - The MIT License (MIT) -Copyright (c) Microsoft Corporation +Copyright (c) All rights reserved. @@ -27,131 +25,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef UF2FORMAT_H -#define UF2FORMAT_H 1 - -#include "uf2cfg.h" - -#include -#include - -#include "app_util.h" - -#include "dfu_types.h" - -#define SD_MAGIC_NUMBER 0x51b1e5db -#define SD_MAGIC_OK() (*((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == 0x51b1e5db) -extern bool sdRunning; - -// All entries are little endian. - -#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" -#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected -#define UF2_MAGIC_END 0x0AB16F30UL // Ditto - -// If set, the block is "comment" and should not be flashed to the device -#define UF2_FLAG_NOFLASH 0x00000001 -#define UF2_FLAG_FAMILYID 0x00002000 - -#define MAX_BLOCKS (FLASH_SIZE / 256 + 100) -typedef struct { - uint32_t numBlocks; - uint32_t numWritten; - uint8_t writtenMask[MAX_BLOCKS / 8 + 1]; -} WriteState; - -typedef struct { - // 32 byte header - uint32_t magicStart0; - uint32_t magicStart1; - uint32_t flags; - uint32_t targetAddr; - uint32_t payloadSize; - uint32_t blockNo; - uint32_t numBlocks; - uint32_t familyID; - - // raw data; - uint8_t data[476]; - - // store magic also at the end to limit damage from partial block reads - uint32_t magicEnd; -} UF2_Block; - -typedef struct { - uint8_t version; - uint8_t ep_in; - uint8_t ep_out; - uint8_t reserved0; - uint32_t cbw_tag; - uint32_t blocks_remaining; - uint8_t *buffer; -} UF2_HandoverArgs; - -typedef void (*UF2_MSC_Handover_Handler)(UF2_HandoverArgs *handover); -typedef void (*UF2_HID_Handover_Handler)(int ep); - -// this is required to be exactly 16 bytes long by the linker script -typedef struct { - void *reserved0; - UF2_HID_Handover_Handler handoverHID; - UF2_MSC_Handover_Handler handoverMSC; - const char *info_uf2; -} UF2_BInfo; - -#define UF2_BINFO ((UF2_BInfo *)(APP_START_ADDRESS - sizeof(UF2_BInfo))) - -static inline bool is_uf2_block(void *data) { - UF2_Block *bl = (UF2_Block *)data; - return bl->magicStart0 == UF2_MAGIC_START0 && bl->magicStart1 == UF2_MAGIC_START1 && - bl->magicEnd == UF2_MAGIC_END; -} - -static inline bool in_uf2_bootloader_space(const void *addr) { - return USER_FLASH_END <= (uint32_t)addr && (uint32_t)addr < FLASH_SIZE; -} - - -#ifdef UF2_DEFINE_HANDOVER -static inline const char *uf2_info(void) { - if (in_uf2_bootloader_space(UF2_BINFO->info_uf2)) - return UF2_BINFO->info_uf2; - return "N/A"; -} - -static inline void hf2_handover(uint8_t ep) { - const char *board_info = UF2_BINFO->info_uf2; - UF2_HID_Handover_Handler fn = UF2_BINFO->handoverHID; - - if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) && - ((uint32_t)fn & 1)) { - // Pass control to bootloader; never returns - fn(ep & 0xf); - } -} - -// the ep_in/ep_out are without the 0x80 mask -// cbw_tag is in the same bit format as it came -static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining, uint8_t ep_in, - uint8_t ep_out, uint32_t cbw_tag) { - if (!is_uf2_block(buffer)) - return; - - const char *board_info = UF2_BINFO->info_uf2; - UF2_MSC_Handover_Handler fn = UF2_BINFO->handoverMSC; - - if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) && - ((uint32_t)fn & 1)) { - UF2_HandoverArgs hand = { - 1, ep_in, ep_out, 0, cbw_tag, blocks_remaining, buffer, - }; - // Pass control to bootloader; never returns - fn(&hand); - } -} -#endif - -#endif #ifndef ARRAYSIZE2_H #define ARRAYSIZE2_H diff --git a/src/usb/uf2/uf2.h b/src/usb/uf2/uf2.h index 6116f3f..6edbb4f 100644 --- a/src/usb/uf2/uf2.h +++ b/src/usb/uf2/uf2.h @@ -152,201 +152,3 @@ static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining #endif #endif - -#ifndef ARRAYSIZE2_H -#define ARRAYSIZE2_H - -#ifndef __has_feature - #define __has_feature(x) 0 // Compatibility with non-clang compilers. -#endif - -#if __cplusplus >= 199711L - #pragma message "using Ivan J. Johnson's ARRAY_SIZE2" - - // Works on older compilers, even Visual C++ 6.... - // Created by Ivan J. Johnson, March 06, 2007 - // See http://drdobbs.com/cpp/197800525?pgno=1 - // - // Pseudocode: - // if x is not an array - // issue a compile-time error - // else - // use the traditional (non-typesafe) C99 COUNTOF expression - // - // If the argument is any of: - // object of class type, such as an std::vector - // floating-point type - // function pointer - // pointer-to-member - // then the first reinterpret_cast<> is not legal (compiler error) - // - // The type for check1 is chosen and named to help understand - // the cause of the error, because the class name is likely to - // appear in the compiler error message. - // - // If check1 succeeds, then the argument must be one of: - // an integral type - // an enumerated type - // a pointer to an object - // an array - // - // Check2 expands approximately to sizeof(check_type(x, &x)), - // where check_type is an overloaded function. - // Because this is purely a compile-time computation, - // the function is never really called or even implemented, - // but it lets the compiler apply overload resolution, - // which allows further type discrimination. - // There are three possibilities to consider: - // x is an integral type or enumerated type. - // In this case, neither of the two function overloads - // is a match, resulting in a compiler error. - // x is a pointer to an object. - // In this case, the first argument to check_type() - // is a pointer and the second one is a pointer-to-pointer. - // The best function match is the first overload of check_type, - // the one that returns an incomplete type (Is_pointer). - // However, because Is_pointer is an incomplete type, - // sizeof(Is_pointer) is not a valid expression, - // resulting in a compiler error. - // x is an array. - // In this case, the first argument to check_type() - // is an array and the second is a pointer-to-array. - // A pointer-to-array is *NOT* convertible to a - // pointer-to-pointer, so the first overload of - // check_type() is not a match. - // However, an array IS convertible to a pointer, - // and a pointer-to-array already is a pointer. - // Any pointer is convertible to a void*, - // so the second function overload is a match. - // That overload returns a complete type (Is_array). - // Because it's a complete type, - // sizeof(Is_array) is a valid expression. - // Thus, the compiler has EXCLUDED every possible type - // except arrays via compilation errors before reaching - // the third line. - // Moreover, check1 and check2 are reduced to the value zero, - // while the third line is the old type-unsafe C-style macro, - // now made entirely type-safe. - // - // Additional benefits: - // The result is itself constexpr - // - // - #define ARRAY_SIZE2(arr) ( \ - 0 * sizeof(reinterpret_cast(arr)) + /*check1*/ \ - 0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + /*check2*/ \ - sizeof(arr) / sizeof((arr)[0]) /* eval */ \ - ) - - struct Bad_arg_to_COUNTOF { - class Is_pointer; // incomplete - class Is_array {}; - template - static Is_pointer check_type(const T*, const T* const*); - static Is_array check_type(const void*, const void*); - }; - -#elif __cplusplus >= 201103L || /* any compiler claiming C++11 support */ \ - _MSC_VER >= 1900 || /* Visual C++ 2015 or higher */ \ - __has_feature(cxx_constexpr) /* CLang versions supporting constexp */ - - #pragma message "C++11 version ARRAY_SIZE2" - - namespace detail - { - template - constexpr std::size_t countof(T const (&)[N]) noexcept - { - return N; - } - } // namespace detail - #define ARRAY_SIZE2(arr) detail::countof(arr) - -#elif _MSC_VER // Visual C++ fallback - - #pragma message "using Microsoft Visual C++ intrinsic ARRAY_SIZE2" - #define ARRAY_SIZE2(arr) _countof(arr) - -#elif __cplusplus >= 199711L && ( /* C++ 98 trick */ \ - defined(__INTEL_COMPILER) || \ - defined(__clang__) || \ - (defined(__GNUC__) && ( \ - (__GNUC__ > 4) || \ - (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \ - ))) - - #pragma message "C++98 version ARRAY_SIZE2" - - template - char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N]; - #define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x)) - -#else - - #pragma message "Using type-unsafe version of ARRAY_SIZE2" - // This is the worst-case scenario macro. - // While it is valid C, it is NOT typesafe. - // For example, if the parameter arr is a pointer instead of array, - // the compiler will SILENTLY give a (likely) incorrect result. - #define ARRAY_SIZE2(arr) sizeof(arr) / sizeof(arr[0]) - -#endif - - -#endif // ARRAYSIZE2_H - - -#ifndef COMPILE_DATE_H -#define COMPILE_DATE_H - -#define __YEAR_INT__ ((( \ - (__DATE__ [ 7u] - '0') * 10u + \ - (__DATE__ [ 8u] - '0')) * 10u + \ - (__DATE__ [ 9u] - '0')) * 10u + \ - (__DATE__ [10u] - '0')) - -#define __MONTH_INT__ ( \ - (__DATE__ [2u] == 'n' && __DATE__ [1u] == 'a') ? 1u /*Jan*/ \ -: (__DATE__ [2u] == 'b' ) ? 2u /*Feb*/ \ -: (__DATE__ [2u] == 'r' && __DATE__ [1u] == 'a') ? 3u /*Mar*/ \ -: (__DATE__ [2u] == 'r' ) ? 4u /*Apr*/ \ -: (__DATE__ [2u] == 'y' ) ? 5u /*May*/ \ -: (__DATE__ [2u] == 'n' ) ? 6u /*Jun*/ \ -: (__DATE__ [2u] == 'l' ) ? 7u /*Jul*/ \ -: (__DATE__ [2u] == 'g' ) ? 8u /*Jul*/ \ -: (__DATE__ [2u] == 'p' ) ? 9u /*Jul*/ \ -: (__DATE__ [2u] == 't' ) ? 10u /*Jul*/ \ -: (__DATE__ [2u] == 'v' ) ? 11u /*Jul*/ \ -: 12u /*Dec*/ ) - -#define __DAY_INT__ ( \ - (__DATE__ [4u] == ' ' ? 0u : __DATE__ [4u] - '0') * 10u \ - + (__DATE__ [5u] - '0') ) - -// __TIME__ expands to an eight-character string constant -// "23:59:01", or (if cannot determine time) "??:??:??" -#define __HOUR_INT__ ( \ - (__TIME__ [0u] == '?' ? 0u : __TIME__ [0u] - '0') * 10u \ - + (__TIME__ [1u] == '?' ? 0u : __TIME__ [1u] - '0') ) - -#define __MINUTE_INT__ ( \ - (__TIME__ [3u] == '?' ? 0u : __TIME__ [3u] - '0') * 10u \ - + (__TIME__ [4u] == '?' ? 0u : __TIME__ [4u] - '0') ) - -#define __SECONDS_INT__ ( \ - (__TIME__ [6u] == '?' ? 0u : __TIME__ [6u] - '0') * 10u \ - + (__TIME__ [7u] == '?' ? 0u : __TIME__ [7u] - '0') ) - - -#define __DOSDATE__ ( \ - ((__YEAR_INT__ - 1980u) << 9u) | \ - ( __MONTH_INT__ << 5u) | \ - ( __DAY_INT__ << 0u) ) - -#define __DOSTIME__ ( \ - ( __HOUR_INT__ << 11u) | \ - ( __MONTH_INT__ << 5u) | \ - ( __DAY_INT__ << 0u) ) - -#endif // COMPILE_DATE_H - From 1b54f0457d918e5a5842c583817c67b1bbac8a72 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 19 Mar 2019 00:37:53 -0700 Subject: [PATCH 7/9] PR review requested changes. Per hathach's request, remove built-in test option. Per hathach's request, remote __TIME__ from info file. --- src/usb/uf2/ghostfat.c | 51 +----------------------------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 01ec685..d3bc863 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -11,9 +11,6 @@ #include "bootloader_settings.h" #include "bootloader.h" -// Uncomment the next line to create an additional 20 files in root. -// #define CREATE_MANY_FILES - typedef struct { uint8_t JumpInstruction[3]; uint8_t OEMInfo[8]; @@ -69,8 +66,7 @@ const char infoUf2File[] = // "Model: " PRODUCT_NAME "\r\n" "Board-ID: " BOARD_ID "\r\n" "Bootloader: " BOOTLOADER_ID "\r\n" - "Date: " __DATE__ "\r\n" - "Time: " __TIME__ "\r\n"; + "Date: " __DATE__ "\r\n"; const char indexFile[] = // "\n" @@ -82,56 +78,11 @@ const char indexFile[] = // "" "\n"; -#ifdef CREATE_MANY_FILES -const char dataFile00[] = "This is the data for File 00\r\n"; -const char dataFile01[] = "This is the data for File 01\r\n"; -const char dataFile02[] = "This is the data for File 02\r\n"; -const char dataFile03[] = "This is the data for File 03\r\n"; -const char dataFile04[] = "This is the data for File 04\r\n"; -const char dataFile05[] = "This is the data for File 05\r\n"; -const char dataFile06[] = "This is the data for File 06\r\n"; -const char dataFile07[] = "This is the data for File 07\r\n"; -const char dataFile08[] = "This is the data for File 08\r\n"; -const char dataFile09[] = "This is the data for File 09\r\n"; -const char dataFile10[] = "This is the data for File 10\r\n"; -const char dataFile11[] = "This is the data for File 11\r\n"; -const char dataFile12[] = "This is the data for File 12\r\n"; -const char dataFile13[] = "This is the data for File 13\r\n"; -const char dataFile14[] = "This is the data for File 14\r\n"; -const char dataFile15[] = "This is the data for File 15\r\n"; -const char dataFile16[] = "This is the data for File 16\r\n"; -const char dataFile17[] = "This is the data for File 17\r\n"; -const char dataFile18[] = "This is the data for File 18\r\n"; -const char dataFile19[] = "This is the data for File 19\r\n"; -#endif // CREATE_MANY_FILES - // WARNING -- code presumes only one NULL .content for .UF2 file // and requires it be the last element of the array static struct TextFile const info[] = { {.name = "INFO_UF2TXT", .content = infoUf2File}, {.name = "INDEX HTM", .content = indexFile}, -#ifdef CREATE_MANY_FILES - {.name = "FILE00 TXT", .content = dataFile00}, - {.name = "FILE01 TXT", .content = dataFile01}, - {.name = "FILE02 TXT", .content = dataFile02}, - {.name = "FILE03 TXT", .content = dataFile03}, - {.name = "FILE04 TXT", .content = dataFile04}, - {.name = "FILE05 TXT", .content = dataFile05}, - {.name = "FILE06 TXT", .content = dataFile06}, - {.name = "FILE07 TXT", .content = dataFile07}, - {.name = "FILE08 TXT", .content = dataFile08}, - {.name = "FILE09 TXT", .content = dataFile09}, - {.name = "FILE10 TXT", .content = dataFile10}, - {.name = "FILE11 TXT", .content = dataFile11}, - {.name = "FILE12 TXT", .content = dataFile12}, - {.name = "FILE13 TXT", .content = dataFile13}, - {.name = "FILE14 TXT", .content = dataFile14}, - {.name = "FILE15 TXT", .content = dataFile15}, - {.name = "FILE16 TXT", .content = dataFile16}, - {.name = "FILE17 TXT", .content = dataFile17}, - {.name = "FILE18 TXT", .content = dataFile18}, - {.name = "FILE19 TXT", .content = dataFile19}, -#endif {.name = "CURRENT UF2"}, }; From 8ab4774c931d01c1e8339061555ce668b57a4d07 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 19 Mar 2019 00:57:24 -0700 Subject: [PATCH 8/9] Manual merge? Pickup a couple changes that don't seem to be propagating? Possible that there's some git merge I missed, but it's simple enough to fix manually. --- src/usb/uf2/ghostfat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index d3bc863..155ecd3 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -210,7 +210,7 @@ void read_block(uint32_t block_no, uint8_t *data) { if (sectionIdx >= SECTORS_PER_FAT) sectionIdx -= SECTORS_PER_FAT; // second FAT is same as the first... if (sectionIdx == 0) { - data[0] = 0xf0; + data[0] = 0xf8; // first FAT entry must match BPB MediaDescriptor // WARNING -- code presumes only one NULL .content for .UF2 file // and all non-NULL .content fit in one sector // and requires it be the last element of the array @@ -223,7 +223,7 @@ void read_block(uint32_t block_no, uint8_t *data) { if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR) ((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1; } - } else if (block_no < START_CLUSTERS) { + } else if (block_no < START_CLUSTERS) { // Requested root directory sector sectionIdx -= START_ROOTDIR; From 66d17704077db9eef0d559cd1a38bb111a546969 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski Date: Tue, 19 Mar 2019 01:01:50 -0700 Subject: [PATCH 9/9] manually add another lost comment --- src/usb/uf2/ghostfat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 155ecd3..6307b8b 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -264,7 +264,7 @@ void read_block(uint32_t block_no, uint8_t *data) { sectionIdx -= START_CLUSTERS; if (sectionIdx < NUM_FILES - 1) { memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content)); - } else { + } else { // generate the UF2 file data on-the-fly sectionIdx -= NUM_FILES - 1; uint32_t addr = USER_FLASH_START + sectionIdx * 256; if (addr < USER_FLASH_START+FLASH_SIZE) {