Все уже реализовано, например тут в
YAM-TOUCH-CAP-V1AБерем любой пример Mass Storage, пишем свои функции чтения и записи сектора.
При заходе в boot проверяем CRC приложения или нажатие кнопки.
Если в CRC ошибка или нажата кнопка запускаем инициализцию USB mass устройства.
При этом выделяем в ОЗУ память для 3-х секторов: MBR FAT и DIR.
MBR составляем так, что у нас 512 байт на сектор, 2 сектора на кластер (это для STM32F103C8T6)
1 резервный сектор который и есть MBR, 1 сектор FAT и элементов в корневом каталоге 512/32=16,
скрытых секторов 0. В качестве серийного номера тома я беру серийный номер изделия.
Далее заполняем таблицу FAT исходя из размеров прошивки или не заполняем если CRC ошибочна.
Заполняем DIR (или не заполняем если CRC ошибочна) нужным Вам именем файла с указанием начального FAT и размера прошивки.
В имя я всегда добавляю версию приложения, чтобы по имени файла можно было понять с чем имеем дело.
Т.е., например "YAM-TOUCH-CAP-V1 Version 3_06.ldr" такое имя будет у версии 3.06.
Так-же в DIR я заполняю VOLUME, где указываю версию загрузчика.
В чтении сектора если номер сектора меньше 3 отдаем то, что у нас в ОЗУ, иначе отдаем все 0

Давать читать прошивку не будем.
В записи сектора если запись в FAT или DIR - пишем себе в ОЗУ, иначе пишем в сектора приложения.
Операционка всегда (что windows, andoid...) пишет файл последовательно...
Весь boot у меня занимает 12288 байт...
Я конечно тут умолчал о том, что прошивка-то пишется зашифрованная и сначала сектора расшифровываются, а потом уже пишутся в область приложения.