Технология TrustZone в STM32L5. Теория и практика
26 марта 2021
Вячеслав Семенов (г. Ростов-на-Дону), Святослав Зубарев (г. Смоленск)
Технологии защиты и безопасности встраиваемых систем постоянно совершенствуются, и компания STMicroelectronics воплощает в своих микроконтроллерах самые передовые технологии. Функция TrustZone ядра ARM Cortex-M33 открывает новые возможности для создания приложений, где требуется высокий уровень защиты программ, соответствующий стандартам PSA. Давайте подробно рассмотрим данную функцию на примере микроконтроллеров нового семейства STM32L5.
Один из методов обеспечения информационной безопасности – изоляция – уже долгое время применяется в современных микроконтроллерах, STM32 производства STMicroelectronics не является исключением. Изоляция – это программно-аппаратный непреодолимый для злоумышленника барьер, обеспечивающий ограничение доступа к доверенным ресурсам микроконтроллера из недоверенной области (рисунок 1). Этими ресурсами могут быть как интерфейсы, области памяти и периферия, так и алгоритмы, ключи шифрования и пароли. Чтобы обеспечить изоляцию, необходимо наличие аппаратных средств, исключающих возможность программных способов нарушения изоляции.

Рис. 1. Диаграмма типового процесса работы приложения с изоляцией: зеленая зона – доверенная область, красная – недоверенная
Типовым процессом работы доверенного проекта являются загрузка, переход к доверенному загрузчику, в котором происходит настройка микроконтроллера, проверка узлов, целостности данных и так далее, а затем – передача управления приложению пользователя. Ключевым здесь является доверенный API, дающий пользователю возможность ограниченного доступа к доверенным ресурсам микроконтроллера. Реализация такого доступа является особо сложной задачей.
Виды изоляции в STM32
Одной из ключевых задач барьера изоляции между доверенной и недоверенной частями является ограничение доступа к областям памяти и периферии. В разных семействах STM32 эта задача решается по-разному.
Одним из наиболее распространенных решений является модуль защиты памяти (Memory Protection Init, MPU) в совокупности с уровнями привилегированности процессов ядра Cortex. MPU – это специализированный модуль, позволяющий определить области памяти, к которым будет ограничен доступ ядра при работе из непривилегированного режима (рисунок 2).

Рис. 2. Модуль защиты памяти MPU
Другим способом является применение Firewall, доступного в сериях L0 и L4. Firewall контролирует все транзакции на шине между областями памяти Flash и SRAM. Если Firewall закрыт, и недоверенное приложение делает попытку доступа к защищенной области памяти, то он фиксирует это событие и генерирует сигнал сброса микроконтроллера. Когда недоверенное приложение использует доверенный API для доступа к некоторым ресурсам доверенного проекта, происходит открытие Firewall, то есть переход к узко ограниченной зоне в защищенной области. Эта четко определенная точка входа позволяет реализовать весь механизм и обеспечить безопасность. Если Firewall обнаружит попытку доступа через какую-либо другую зону, также будет сгенерирован сигнал сброса (рисунок 3).

Рис. 3. Схема работы Firewall
Еще одним способ изоляции является Secure Flash, работающий только на уровне Flash-памяти. После подачи питания или сброса микроконтроллер загружается из доверенной части памяти, где выполняется доверенная загрузка (Secure Boot). После проверки целостности кода и аутентификации приложения пользователя выполнение программы переходит к недоверенной области, а зона Secure Boot становится абсолютно невидимой для системы до следующего сброса. Полностью пропадает возможность доступа к этой зоне из какой-либо другой области. Главный недостаток данной технологии в том, что если требуется получить доступ к данным, хранящимся в зоне Secure Boot, необходимо инициировать сброс питания, что усложняет алгоритм и требует дополнительных ресурсов.

Рис. 4. Работа Secure Flash
Шагом вперед является новая технология ARM – TrustZone. Она основана на фильтрации транзакций шины и отслеживании уровня доступа ядра.
Концепция TrustZone
TrustZone – это дополнительная функция ядра Cortex-M33, основанного на архитектуре ARM-V8M. Ядро Cortex®-M33 является частью группы 32-битных RISC-ядер Arm Cortex®-M. Оно реализует магистральную архитектуру Armv8-M и имеет 3-ступенчатый конвейер. В дополнение к скалярным целочисленным инструкциям, оно также поддерживает операции с плавающей запятой одинарной точности и целочисленные инструкции SIMD для улучшения производительности алгоритмов DSP. В процессоре реализована технология ARM TrustZone® с использованием расширения безопасности ARMv8-M, поддерживающего доверенные и недоверенные состояния.
TrustZone контролирует шину данных с учетом режима безопасности ядра. Данная функция сочетает качества рассмотренных выше способов изоляции, позволяет гибко настраивать области памяти и периферию, вплоть до отдельных выводов GPIO, разделяя их на доверенные и недоверенные (рисунок 5). Благодаря аппаратной поддержке TrustZone работает в реальном времени и обеспечивает малое время задержек на прерываниях, а переключение контекста делает быстрым и предсказуемым.

Рис. 5. Разделение на доверенное (а) и недоверенное (б) пространство
Система всегда запускается в доверенном режиме, если функция TrustZone активна, иначе микроконтроллер работает как при архитектуре ARM V7-M, без каких-либо разделений. Код, исполняемый в доверенном состоянии, имеет доступ как к доверенной, так и к недоверенной информации. Разметка памяти определяет границы доверенной и недоверенной областей, переходы между которыми осуществляются автоматически, что снижает нагрузку на процессор.
Особенности TrustZone:
- два дополнительных стека и два дополнительных регистра указателей;
- аппаратная проверка заполнения стека;
- распределение памяти на доверенные и недоверенные области с помощью SAU (Security Attribution Unit);
- аппаратная обработка прерываний при переходе между доверенной и недоверенной зонами;
- инструкция доверенного шлюза (Secure Gateway, SG).
Переходы между доверенным и недоверенным пространствами в TrustZone V8-M
Когда статус доверенности исключения/прерывания совпадает со статусом процессора, все происходит практически также, как и в процессорах на основе V7-M.
Рассмотрим отличную ситуацию. Например, когда возникает недоверенное прерывание, а процессор выполняет доверенный код, то процессор:
1) отправляет всю доверенную информацию в доверенный стек;
2) стирает содержимое регистров, чтобы исключить утечку защищаемой информации.
Следует отметить, данный порядок действий повышает задержку на прерывании с 12 до 21 тактов.
На рисунке 6 показаны все возможные переходы. Восемь переходов, обозначенных розовыми стрелками, требуют изменения статуса доверенности. Если уровень приоритета остается неизменным, например, при переходе из недоверенного режима потока в доверенный, могут использоваться простые вызовы и возвраты функций. В противном случае требуется вход в прерывание и возврат из него.

Рис. 6. Граф переходов между состояниями
Взаимодействие кода и областей памяти разных статусов
С помощью модуля назначения атрибутов доверенности (Security Attribution Unit, SAU) можно программно настроить доверенность до 8 областей памяти и аппаратно — определенный модуль атрибуции (Implementation Defined Attribution Unit, IDAU), который позволяет назначать атрибут защиты на аппаратно фиксированных областях памяти. Это дает возможность задать области памяти как:
- доверенная область (S – Secure);
- область недоверенного вызова (NSC – Non-secure callable);
- недоверенная (NS – Non-secure).
Рисунок 7 показывает, как могут совершаться вызовы из разных типов памяти.

Рис. 7. Вызовы из разных типов памяти
Недоверенная память содержит недоверенное приложение, которое не может напрямую совершать вызовы из доверенной области. Оно может совершить вызов из области недоверенного вызова, оттуда получить доступ к доверенной части и вернуть результат уже напрямую в недоверенную область. Чтобы это сделать, необходимо вызвать функцию, находящуюся в области недоверанного вызова:
BL Func_A_entry
Первой инструкцией в данном случае будет “SG” – Security Gate (доверенный шлюз), а затем переход к доверенному пространству:
Func_A_entry
SG; // Для корректного доступа
B Func_A
Функция в доверенном пространстве по завершении своей работы производит возврат в недоверенное пространство:
Func_A
… ; // Тело функции
BXNS LR
Рассмотрим, как можно задать конфигурацию доверенности для памяти. Как было сказано выше, IDAU статично определяет области с атрибутом NSC, недоверенные NS, а также свободные, с размером, кратным 64 Мбит. SAU позволяет задать до 8 областей, которые могут переопределить зоны IDAU в целях утверждения доверенных зон либо подтвердить их недоверенность. На рисунке 8 представлен пример конфигурации областей.

Рис. 8. Пример задания атрибутов доверенности и взаимодействия SAU/IDAU
Сочетание конфигураций SAU/IDAU определяет результат разметки памяти, показанный в столбце «FINAL». При несовпадении атрибутов SAU переопределяет IDAU. Стоит обратить внимание на области Alias-NS и Аlias-S под номерами 1 и 2 в левой части рисунка 8. Об их важности будет сказано далее.
Для лучшего понимания рассмотрим зонирование памяти в другом ключе. Рассмотрим конфигурацию памяти на примере. Имеется физическая память, которую необходимо полностью разметить атрибутами NS, NSC и S. Наша цель – получить разметку, как изображено на рисунке 9.

Рис. 9. Целевая картина разметки памяти
На рисунке 10 мы видим исходную разметку памяти по IDAU, в которой есть зоны Alias-NS и Alias-NSC. «Alias» означает «наложение», смысл и применение Alias-областей рассмотрим несколько позже.

Рис. 10. Разметка в IDAU
SAU по умолчанию определяет всю память как доверенную. Программисту необходимо переопределить в SAU, какие зоны будут являться NSC, NS или свободными от атрибутов. Пример такого переопределения показан на рисунке 11.

Рис. 11. Переопределение с помощью SAU
Результатом совместной конфигурации IDAU/SAU является разметка, представленная в правой части рисунка 12.

Рис. 12. Финальная разметка памяти
Виды доступа
Когда у нас есть физическая память и разметка зон доверенности для нее, можно рассмотреть комбинации типов областей и режимов процессора на предмет прав доступа. Рисунок 13 иллюстрирует возможные комбинации.

Рис. 13. Комбинации условий доступа
Здесь вступает в силу значение Alias-областей. В соответствии с рисунком 9, нижний диапазон адресов 0x08000000…0x0BFFFFFF является Alias-Secure, а верхний диапазон 0x0C000000…0x0FFFFFFF является Alias-NSC. Этим обусловлено различие в результате попытки доступа в аналогичные области памяти. Таблица 1 консолидирует совокупности атрибутов и режимов процессора и соответствующих им результатов попытки доступа.
Таблица 1. Варианты доступа
Alias зона | Режим процессора | Атрибут | Результат доступа |
---|---|---|---|
Alias-NSC | Secure mode | Non-secure (NS) | NOK |
Non-secure mode | NOK | ||
Secure mode | Non-secure callable (NSC) | OK | |
Non-secure mode | OK | ||
Secure mode | Secure (S) | OK | |
Non-secure mode | NOK | ||
Alias-Secure | Secure mode | Non-secure (NS) | OK |
Non-secure mode | OK | ||
Secure mode | Non-secure callable (NSC) | NOK | |
Non-secure mode | NOK | ||
Secure mode | Secure (S) | NOK | |
Non-secure mode | NOK |
Защита от чтения
RDP 0.5 – это режим безопасности, позволяющий производить отладку только недоверенного пространства. Отладчик не сможет получить доступ к доверенной области. Если представить, что RDP 0.5 нет, то из уровня 0 можно перейти на уровень 1, на котором нет возможности выбрать способ загрузки, нет связи с отладчиком. Если произвести возврат с уровня 1 на уровень 0, то произойдет полное стирание памяти. При переходе с уровня 1 на уровень 2 становятся невозможными какие-либо изменения, отсутствует связь, нет возможности изменять байты опций защиты, микросхема полностью закрывается. Когда мы вводим уровень 0.5, появляются дополнительные возможности. При переходе с уровня 0 на уровень 0.5 мы теряем полный доступ к доверенной зоне и можем лишь подключиться к недоверенной зоне и отлаживать ее. С уровня 0.5 мы можем перейти на уровень 1 с полной защитой, а возврат на один из нижних уровней выглядит иначе. Если происходит переход с уровня 0.5 на уровень 0, то выполняется полное стирание всей памяти. При переходе с уровня 1 на уровень 0.5 стирается лишь недоверенная область памяти. Таким образом, RDP Level 0.5 позволяет работать в недоверенном пространстве, не затрагивая доверенное.
Активация TrustZone
Активация данной функции осуществляется посредством установки бита TZEN в его байте опций. SAU конфигурируется на этапе загрузки системы. Кроме того, на данном этапе можно сконфигурировать функции защиты Flash-памяти:
- области памяти WATERMARK (защита на основе водяного знака), определяющие доверенные и HDP-зоны;
- блочная защита памяти, с помощью которой можно конфигурировать доверенность страниц памяти в процессе работы;
- дополнительный уровень защиты от чтения RDP level5.
Деактивация TrustZone
Деактивация производится путем сброса бита TZEN, что возможно только при понижении защиты RDP с уровня 1 до уровня 0, что влечет за собой полное стирание памяти. Когда TrustZone деактивируется после загрузки байтов опций, также отключаются следующие функции защиты:
- область памяти с WATERMARK защитой;
- блочная защита SECBB;
- RDP level5;
- доверенное прерывание.
Также все доверенные регистры становятся RAZ/WI (Read-as-zero, Writes Ignored) – при чтении возвращают нули, запись игнорируется.
TrustZone в МК STM32L5
В практической части мы рассмотрим работу с защищенной и незащищенной областями памяти микроконтроллера STM32L5 на примере отладочной платы NUCLEO-L552ZE-Q:
- проведем настройку байтов конфигурации при помощи утилиты STM32CubeProgrammer: активируем TrustZone, настроим защищенную и незащищенную области памяти;
- создадим тестовый проект в среде STM32CubeIDE, в котором будем управлять светодиодом, инициализированным в защищенной области памяти из незащищенной при помощи пользовательской кнопки;
- проверим работу программы при активации RDP уровня 0.5, ограничивающего отладку и выполнение только незащищенной областью.
Активация TrustZone на плате NUCLEO-L552ZE-Q
Для активации TrustZone первым делом подключим плату к ПК и запустим утилиту STM32CubeProgrammer (рисунки 14, 15).
Следующим шагом будет переход в раздел Optional bytes, далее в раздел User Configuration, и проверка установки бита DBANK. Его установка свидетельствует о том, что наша Flash-память разбита на два банка (рисунок 16).
В данном разделе также проведем активацию TrustZone, установив соответствующий бит данных и нажмем «Apply» для сохранения конфигурации (рисунок 17).
После активации TrustZone мы видим, что появились разделы Secure Area 1 и Secure Area 2, определяющие области памяти, доступные для работы. По умолчанию их диапазоны установлены как SECWM*_PSTRT Value = 0x0 и SECWM*_PEND Value = 0x7f в обоих случаях, что свидетельствует о том, что обе области являются защищенными. Для того чтобы область стала незащищенной, необходимо, чтобы значение байта SECWM*_PSTRT было больше SECWM*_PEND. Установим для второй области SECWM2_PSTRT Value = 0x7f и SECWM2_PEND Value = 0x0 и нажмем «Применить» (рисунок 18).
На этом настройки TrustZone окончены, перейдем к созданию тестового проекта в среде в STM32CubeIDE.
Создание тестового проекта в STM32CubeIDE
Запустим STM32CubeIDE и создадим новый проект для платы NUCLEO-L552ZE-Q (рисунок 19).
Нажмем «Next» и зададим имя проекта, а также активируем функции TrustZone, остальные настройки оставим по умолчанию (рисунок 20).

Рис. 20. Установка имени проекта и активация функций TrustZone
Согласно документации на NUCLEO-L552ZE-Q, пользователю для работы доступна одна кнопка (GPIO PC13) и 3 светодиода: LD1 – GPIO PA5, LD2 – GPIO PB7 и LD3 – GPIO PA9. Для работы с ними необходимо активировать соответствующие GPIO.
Назначим PC13 функцию входа (рисунок 21).
Далее установим PB7 как выход (будем использовать светодиод LD2), как показано на рисунке 22.
Перейдем в настройки GPIO и назначим Pin Context Assignment для GPIO PC13 и GPIO PB7 как «Cortex-M33 non secure» и «Cortex-M33 secure», соответственно (рисунки 23, 24).
Сохраним проект (Ctrl + F5), откроем файл main.с в разделе TrustZone_test_Secure и добавим следующий код сразу после инициализации портов GPIO (рисунок 25):
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
Теперь при включении платы светодиод LD2 выполнит приветственное мигание, что подтвердит его работоспособность.
Для работы с портом GPIO PB7 из незащищенной области создадим функцию типа CMSE_NS_ENTRY в файле secure_nsc.c (рисунок 26):
CMSE_NS_ENTRY void Secure_Toggle_Pin(void)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
}
Чтобы получить к ней доступ из других областей проекта, добавим ее инициализацию в файл secure_nsc.h (рисунок 27):
void Secure_Toggle_Pin(void);
Далее перейдем в раздел TrustZone_test_NonSecure и в файле main.c добавим проверку состояния нашей кнопки (GPIO PС13). Если кнопка нажата, вызовем функцию Secure_Toggle_Pin(), после чего должен загореться светодиод LD2 (рисунок 28):
If (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)==1)
{
Secure_Toggle_Pin();
HAL_Delay(500);
}
Выполним сохранение и компиляцию проекта (рисунок 29).
Далее зайдем в настройки откладки для TrustZone_test_Secure и добавим туда раздел TrustZone_test_NonSecure, как это показано на рисунках 30…32.

Рис. 32. Конечный результат настроек
Запустим отладку и проверим работоспособность нашей программы: при запуске платы светодиод LD2 должен мигнуть, после чего он вновь загорится только после нажатия пользовательской кнопки (рисунки 33, 34).

Рис. 33. Состояние платы NUCLEO-L552ZE-Q до нажатия пользовательской кнопки

Рис. 34. Состояние платы NUCLEO-L552ZE-Q после нажатия пользовательской кнопки
Проверка программы при активации RDP и отключение TrustZone
Для проверки работы включим защиту RDP уровня 0.5. Данный уровень защиты позволяет проводить отладку только в незащищенной области и доступен только при активации TrustZone. Для активации защиты снова запустим STM32CubeProgrammer и перейдем в раздел Option bytes, где зададим RDP значение, равное 55 (рисунок 35).
Произведем сброс питания платы и проверим работоспособность нашего приложения (рисунки 36, 37).

Рис. 36. Состояние платы NUCLEO-L552ZE-Q до нажатия пользовательской кнопки, RDP Level 0.5

Рис. 37. Состояние платы NUCLEO-L552ZE-Q после нажатия пользовательской кнопки, RDP Level 0.5
Деактивация TrustZone происходит снятием соответствующего бита конфигурации, как это было ранее показано на рисунке 4.
Заключение
Мы подробно рассмотрели функцию TrustZone ядра Cortex-M33 с архитектурой ARMV8-M на примере микроконтроллеров серии STM32L5 – разобрали способы активации и деактивации, разделение памяти, атрибуты безопасности и их влияние на исполнение программного кода в различных режимах работы процессора. В ходе практической работы закрепили знания о работе с TrustZone на примере управления светодиодом по нажатию кнопки – создали проект в среде STM32CubeIDE, прошили микроконтроллер, получили наглядное представление о различии режимов работы TrustZone, особенностях применения защиты от чтения RDP level 0.5 и корректной деактивации TrustZone.
Среди прочих функций защиты и методов изоляции STM32 TrustZone занимает одно из ключевых мест в обеспечении информационной безопасности приложений. Данная функция предоставляет гибкие возможности для управления памятью в контексте ее защиты и корректного распределения прав доступа, в соответствии с нуждами каждой конкретной задачи, а также прекрасно обеспечивает требуемый уровень безопасности приложения, не ограничивая разработчика какими-либо фиксированными настройками и не усложняя процесс разработки слишком сложной иерархией способов, методов и уровней защиты.
Функция TrustZone в микроконтроллерах STMicroelectronics доступна не только в серии STM32L5. Недавно было анонсировано новое семейство малопотребляющий микроконтроллеров STM32U5 на базе ядра ARM Cortex-M33. Благодаря улучшенному техпроцессу и специальной архитектуре модели STM32U5 будут иметь рекордно малое потребление и так же, как STM32L5, соответствовать высоким требованиям стандартов безопасности PSA.
Литература
- AN5347. STM32L5 Series TrustZone® features
- RM0438. STM32L552xx and STM32L562xx advanced Arm®-based 32-bit MCUs
Наши информационные каналы