Меню Рубрики

Stm32 bootloader как написать

Пишем свой бутлоадер для STM32.

В одной из прошлых статей описывал как прошить STM32 с помощью бутлоадера и объяснял принцип его работы. Для тех кто её не читал напомню, что бутлоадер, по сути, это программа, которая живёт в МК и может перезаписывать его память, то есть обновлять прошивку.

Думаю у кого-то мог возникнуть вопрос, зачем писать бутлоадер самому, если можно пользоваться встроенным?
Ответ на этот вопрос очень прост, пока своими поделками на МК пользуешься сам — писать бутлоадер нет надобности, в любой момент можно взять поделку и залить в неё новую прошивку. Но если устройство становится коммерческим, то встаёт вопрос как обновлять прошивку пользователям?

Если выложить прошивку в открытый доступ, то велика вероятность того, что кто-то скопирует устройство и организует продажи. Разработчику такой ситуации хотелось бы избежать, но в то же время хотелось бы радовать пользователей новыми фичами и исправлением старых багов, для решения этих вопросов и нужен самописный бутлоадер.

Используя самописный бутлоадер, ничто не мешает нам в нём инициализировать часть периферии, необходимой для работы устройства и не инициализировать её в прошивке. Таким образом, прошивка, отправляемая пользователю, получается неполноценной и на МК без нашего бутлоадера работать не будет. Для повышения безопасности прошивку можно шифровать, но ни один из способов не поможет если кому-то действительна понадобиться ваша прошивка. Судя по данным, найденным в интернете, есть конторы которые за определённую плату(в районе 1000$) извлекают прошивку из залоченного МК.

С теорией разобрались, предлагаю перейти к практике. Условно наш бутлоадер будет иметь следующую структуру

В зависимости от того с какого носителя или каким способом мы будем обновляться, код в скобках условного оператора будет меняться, неизменным будет только переход в основную программу, с него предлагаю и начать.

Сразу хотелось бы отметить, что наш бутлоадер будет располагаться во флэше и в дальнейших рассуждениях это подразумевается по умолчанию.
При включении питания МК всегда стартует с начала флэш памяти, а именно с адреса 0х0800000. Далее, из бутлоадера надо прыгнуть в основную программу, расположение которой зависит от размера бутлоадера. Для того чтобы вычислить адрес начала основной программы надо к 0х0800000 прибавить размер бутлоадера.

В общем перед нами стоит два вопроса:
На какой адрес необходимо прыгнуть ?
Как реализовать прыжок ?

Прыжок реализуется с помощью указателя на функцию. Как мы знаем указатель — это адрес, а в указателе на функцию хранится адрес, с которого начинается функция.
Для ответа на первый вопрос надо вспомнить как происходит загрузка МК.

При включении питания МК читает значение по адресу 0x08000000 и записывает его в SP (SP – регистр, который указывает на вершину стека), после чего читает значение по адресу 0x08000004 и записывает его в PC (PC – регистр, который указывает на текущую инструкцию + 4 байта).

Получается, что адрес перехода хранится в векторе Reset Handler. Функция, реализующая прыжок, выглядит следующим образом.

С переходом в основную программу разобрались, осталось научиться обновлять прошивку. Тут важно помнить, что писать можно только в чистые страницы, то есть перед обновлением прошивки, отведённые под неё страницы надо очистить.

Остался не раскрыт еще один вопрос, как передавать прошивку?
Изначально решал эту задачу в лоб, передавая бинарник, в начале которого указывал размер прошивки, но позже понял, что этот вариант неудобный и неправильный. Неудобный он тем, что после генерации бинарника из него надо удалять всё лишнее, то есть если основная программа начинается с адреса 0x08008400, то все пространство до этого адреса будет заполнена FF.

Из скриншота понятно, сколько занимает основная программа и сколько в ней мусора. А неправильный он тем, что прошивка пишется в МК последовательно, но ведь отдельные её части могут лежать где угодно, например картинка, может храниться в конце флеша, тогда получается мы вместо того, чтобы записать картинку по нужному адресу, должны будем последовательно заполнять память МК значениями FF, пока не дойдем до нужного адреса.
Гораздо проще передавать хекс потому, что он состоит из записей(на самом деле их 5 типов, тут описывается структура записи с данными) и в каждой записи указывается размер данных, адрес по которому должны быть записаны данные и сами данные, то есть все ясно и понятно.

Хорошо было бы закончить статью рабочим примером, но мы не разобрали типы записей и не определились со способом обновления. По этому ниже будет описана структура бутлоадера с учетом полученных знаний, а в следующей статье мы разберемся с типами записей и дополним её.

Источник статьи: http://hubstub.ru/stm32/159-pishim-svoy-butlouder-dlya-stm32.html

Загрузчик с шифрованием для STM32

В данной статье хотел бы написать о своем опыте создания загрузчика для STM32 с шифрованием прошивки. Я являюсь индивидуальным разработчиком, поэтому нижеприведенный код может не соответствовать каким-либо корпоративным стандартам

В процессе работы ставились следующие задачи:

  • Обеспечить обновление прошивки пользователем устройства с SD-карты.
  • Обеспечить контроль целостности прошивки и исключить запись некорректной прошивки в память контроллера.
  • Обеспечить шифрование прошивки для исключения клонирования устройства.

Код писался в Keil uVision с использованием библиотек stdperiph, fatFS и tinyAES. Подопытным микроконтроллером был STM32F103VET6, но код может быть легко адаптирован под другой контроллер STM. Контроль целостности обеспечивается алгоритмом CRC32, контрольная сумма расположена в последних 4 байтах файла с прошивкой.

В статье не описано создание проекта, подключение библиотек, инициализация периферии и прочие тривиальные этапы.

Для начала стоит определиться с тем, что такое загрузчик. Архитектура STM32 подразумевает плоскую адресацию памяти, когда в одном адресном пространстве находится Flash-память, RAM, регистры периферии и всё остальное. Загрузчик — это программа, которая начинает выполняться при запуске микроконтроллера, проверяет, нужно ли выполнить обновление прошивки, если нужно — выполняет его, и запускает основную программу устройства. В данной статье будет описан механизм обновления с SD-карты, но можно использовать любой другой источник.

Шифрование прошивки производится алгоритмом AES128 и реализовано при помощи библиотеки tinyAES. Она представляет из себя всего два файла, один с расширением .c, другой с расширением .h, поэтому проблем с её подключением возникнуть не должно.

После создания проекта следует определиться с размерами загрузчика и основной программы. Для удобства размеры следует выбирать кратно размеру страницы памяти микроконтроллера. В данном примере загрузчик будет занимать 64 Кб, а основная программа займет оставшиеся 448 Кб. Загрузчик будет размещаться в начале Flash-памяти, а основная программа сразу после загрузчика. Это следует указать в настройках проекта в Keil. Загрузчик у нас начинается с адреса 0x80000000 (именно с него STM32 начинает выполнение кода после запуска) и имеет размер 0x10000, указываем это в настройках.

Основная программа будет начинаться с 0x08010000 и заканчиваться на 0x08080000 для удобства сделаем define со всеми адресами:

Так же внесем в программу ключи шифрования и инициализационный вектор AES. Данные ключи лучше сгенерировать случайным образом.

В данном примере вся процедура обновления прошивки построена в виде конечного автомата. Это позволяет в процессе обновления отображать что-то на экране, сбрасывать Watchdog и выполнять любые другие действия. Для удобства сделаем define с основными состояниями автомата, чтобы не путаться в числах:

После инициализации периферии нужно проверить необходимость обновления прошивки. В первом состоянии производится попытка чтения SD-карты и проверка наличия файла на ней.

Теперь нам нужно провести проверку прошивки на корректность. Здесь сначала идет код проверки контрольной суммы, выполняющийся при окончании чтения файла, а потом само чтение. Возможно, так писать не следует, напишите в комментариях что вы об этом думаете. Чтение производится по 2 Кб для удобства работы с Flash-памятью, т.к. у STM32F103VET6 размер страницы памяти 2 Кб.

Теперь, если прошивка не повреждена, то нужно её снова прочитать, но на этот раз уже записать во Flash — память.

Теперь для красоты создадим состояния для обработки ошибки и успешного обновления:

Функцию запуска основной программы ExecMainFW() стоит рассмотреть подробнее. Вот она:

Сразу после запуска startup файл все переинициализировал, поэтому основная программа должна вновь выставить указатель на вектор прерывания внутри своего адресного пространства:

В проекте основной программы нужно указать правильные адреса:

Вот, собственно, и вся процедура обновления. Прошивка проверяется на корректность и шифруется, все поставленные задачи выполнены. В случае потери питания в процессе обновления устройство, конечно, закирпичится, но загрузчик останется нетронутым и процедуру обновления можно будет повторить. Для особо ответственных ситуаций можно заблокировать на запись страницы, в которых находится загрузчик через Option bytes.

Однако, в случае с SD-картой можно организовать для самого себя в загрузчике одно приятное удобство. Когда тестирование и отладка новой версии прошивки завершена, можно заставить само устройство по какому-то особому условию (например, кнопка или джампер внутри) зашифровать и выгрузить на SD-карту готовую прошивку. В таком случае останется только извлечь SD-карту из устройства, вставить в компьютер и выложить прошивку в интернет на радость пользователям. Сделаем это в виде ещё двух состояний конечного автомата:

Вот, собственно и всё, что я хотел рассказать. В завершении статьи хотел бы пожелать вам после создания подобного загрузчика не забыть включить защиту от чтения памяти микроконтроллера в Option bytes.

Источник статьи: http://habr.com/ru/post/432966/

24. STM32. Программирование STM32F103. Bootloader

В этой статье мы рассмотрим что такое Bootloader, зачем и когда нужен собственный Bootloader, пример как написать собственный Bootloader, пример как адаптировать прошивку для использования с Bootloader-ом.

Что такое Bootloader?

Первичный Bootloader

Вторичный Bootloader

Свой собственный Bootloader. Как это работает

Bootloader в режиме USB Mass Storage

Поясню. У нас всего 64 Кб, пусть 10Кб займет Bootloader, остается 54Кб. Половину отдадим под Mass Storage, это будет 27Кб. Из этого объема FAT12 откусит 20Кб, останутся 7Кб под файл прошивки. То есть на Mass Storage не влезет файл больше 7Кб. Если оптимизировать распределение памяти и отдать под Mass Storage 38Кб, тогда под программы останется 16 Кб. Это для нас тоже не приемлемо. Поэтому такое решение для нашего микроконтроллера, с небольшим объемом Flash-а, не подходит. Но поблагодарим автора! Именно фрагменты этого проекта я использовал для создания примера Bootloader-а описанного в этой статье. Дальнейшие поиски привели меня к Bootloader-у описанного в статье http://easyelectronics.ru/proshivka-arm-cortex-m3-na-primere-stm32-i-lpc1300.html Шикарный замысел, но реализация полностью не приемлема. Контроллер действительно видно как MASS STORAGE диск, на него вроде бы можно залить новую прошивку, но корректно это происходит только когда файл копировать программой FAR. И только FAR! Удивительно, но простое копирование файла происходит по-разному если это делать разными файл-менеджерами. Операционная система Ubuntu вообще не смогла смонтировать устройство. Поправить что-то в этом Bootloader-е нельзя, поэтому эта реализация тоже была отвергнута. Пришлось писать свой Bootloader. Почему именно USB Mass Storage? Скажем, я делаю не коммерческий проект и хочу чтобы для обновления прошивки конечным пользователям не нужно было искать специфический софт и драйверы. Для работы с Mass Storage устройствами операционные системы используют стандартные драйверы, поэтому с драйверами проблем вообще нет. Обновление прошивки может происходить на разных операционных системах поэтому желательно чтобы программы для обновления прошивки вообще не надо было, или использовались стандартные решения. В этом случае простое копирование файла — наиболее красивое решение. Забегая вперед скажу, что не все так просто. Мне пока не удалось реализовать этот замысел в идеальном виде. Поэтому, желательно чтобы процедура заливки прошивки была более или менее стандартной и, как я сказал, можно было обойтись стандартными командами или стандартным программным обеспечением. Продолжаем разбираться .

USB Mass Storage пример

Давайте покопаемся в коде и разберемся как выполняется работа с Mass Storage. Оказывается микроконтроллер вообще ничего не знает о FAT, кластерах и так далее. Он работает только как посредник — передает данные с USB во Flash, и с Flash в USB. А куда и что писать, собственно, решает операционная система. Отдавать 20Кб флэш памяти микроконтроллера для FAT — это многовато. А что вообще можно придумать для реализации Bootloader-а в режиме USB Mass Storage? Фактически нам нужно выполнять только одну операцию — копирование одного файла. Можно даже с фиксированным именем. Можно обманывать операционную систему и программно формировать данные FAT, но фактически не хранить ее. Для одного файла в корне с фиксированным именем это вполне реальная идея для экономии памяти. При копировании файла на наш USB Mass Storage надо будет лишь записать данные во флэш. Но я прикинул сколько придется углубляться в «муляж» FAT12 и сколько выгребать «граблей», что решил пока отложить этот замысел. Решим задачу более простым путем. Я не нашел в Интернете чтобы так кто-то делал, поэтому говорю что это моя собственная идея. Мне приятно так думать, но у меня есть подозрение что я не первый такой хитрый 🙂

Главная идея нашего USB Mass Storage Bootloader

Команда для заливки на Mass Storage bin-файла под Ubuntu: Где: my.bin — файл прошивки в формате bin /dev/sdc — USB Mass Storage устройство Выяснить имя устройства можно с помощью команды: Я знаю точный размер, поэтому можно грепнуть по размеру: или найти по ID производителя: Под Windows все проще. Скачиваем и устанавливаем программу Win32DiskImager. Выбираем bin — файл для заливки, диск на который будем заливать и нажимаем «Write».

Итак, наш USB Mass Storage, который теперь играет роль Bootloader-а, прошивку уже обновляет. Теперь осталось доделать USB Mass Storage пример таким образом, чтобы он мог передавать управление залитой программе. Я это сделал с помощью кнопки. Когда мы подключаем микроконтроллер к компьютеру USB кабелем, и при этом нога PB1 замкнута на землю, контроллер стартует как USB Mass Storage устройство и можно заливать прошивку. Если во время старта PB1 на землю не замкнута, а висит в воздухе, или подключена к +, микроконтроллер передает управление залитой прошивке. Вот как выглядит код нашего Bootloader-а (доработанного примера USB Mass Storage): Проект можно скачать здесь: https://github.com/avislab/STM32F103/tree/master/Example_Bootloader

Коррекция проектов для работы с Bootloader-ом

Еще надо перенести таблицу векторов прерываний. Для этого в первых строках программы надо добавить следующую функцию:

Это все. Теперь компилируем проект и получаем готовый для заливки bin-файл. Пример проекта можно скачать здесь: https://github.com/avislab/STM32F103/tree/master/Example_First_Programm_for_Bootloader

Резюме

Как это выглядит для разработчика

  • Заливаем обычным программатором в микроконтроллер Bootloader как обычную программу;
  • Готовим и компилируем проект с учетом адреса начиная с которой будет находиться основная программа (зависит от размера Bootloader-а);
  • Готовый bin-файл заливаем в микроконтроллер как это будет делать конечный пользователь. Смотри ниже .

Как это выглядит для пользователя

Когда пользователю нужно обновить прошивку устройства он выполняет следующие действия:

  • Получает от разработчика новый bin-файл;
  • Подключает устройство USB-кабелем к компьютеру при этом удерживает нажатой кнопку. Это может быть любая кнопка управления вашим устройством. Если в устройстве не используются кнопки, это может быть какой-то переключатель или перемычка, которые дадут сигнал Bootloader-у что надо перейти в режим обновления прошивки. В примере Bootloader-а надо замкнуть PB1 на землю. Bootloader переходит в режим USB Mass Storage, операционная система видит диск размером 54Кб;
  • с помощью программы Win32DiskImager (Windows) или dd (для Ubuntu) пользователь записывает bin-файл на диск;
  • отключает устройство от компьютера и включает. Новая прошивка работает.

Таким образом конечному пользователю для обновления прошивки не нужен программатор, не нужно устанавливать драйверы, а для обновления прошивки достаточно использовать стандартные команды (для Windows — почти стандартное программное обеспечение). Что более чем достаточно для не коммерческого продукта. Вы можете модифицировать и оптимизировать этот пример для решения своих задач. Успехов!

Привет!
Случайно совершенно наткнулся на эту статью!
Рад слышать, что моя реализация (STM32 USB Mass Storage Bootloader) была полезна =)
Да, память она жрет. И самое печальное, что не получится прошить бинарник, чей размер > 0.5 FLASH_SIZE. Хотя я в основном использую самые старшие камни f103 серии, а это 1МБ флеша, так что можно себе позволить. К тому же, mass storage диск использую еще много для чего — разные файлы конфигурации, ключи шифрования и т.д.

Может быть есть еще какие-то идеи, как совсем доделать до ума этот бут? У меня одним из условий было, что вообще нельзя использовать никакой софт сторонний. Заливка прошивки — drag-and-drop.

(Дублирую тут комментарий с Youtube для истории.)

Спасибо за статью! Я работаю в Eclipse Neon.3 + GNU ARM, под неё и адаптировал Ваш проект. Обнаружил проблему — если я загружаю по USB прошивку, собранную как Release, всё работает отлично, однако Debug сборка того же исходного текста не работает. К сожалению, системных отдадчиков пока не имею, поэтому отследить причину не могу. Может быть Вы сталкивались с подобным?
Еще наверное Вам будет интересно, что применив оптимизационные флаги -Os и -flto, мне удалось уместить bin файл загрузчика в 8кб (8 040 байт).

Источник статьи: http://blog.avislab.com/stm32-bootloader_ru/


0 0 голоса
Article Rating
Подписаться
Уведомить о
guest

0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии