ФЭНДОМ


Процессоры версии ARMv8-M могут иметь необязательное расширение безопасности (security extension). Ниже описываются особенности таких процессоров.

Режимы

В любой момент времени процессор может находиться либо в безопасном, либо в небезопасном режиме; «внутри» каждого из них при этом поддерживаются обычные режимы обработчика и потока, а для последнего – ещё и привилегированный и непривилегированный режимы.

Код в зависимости от своего местоположения делится на безопасный и небезопасный. Безопасность кода определяется тем, в каком регионе памяти – безопасном или небезопасном – он находится. В безопасном режиме возможна выборка и исполнение кода только из безопасного региона, в небезопасном – только из небезопасного. Для программного перехода между режимами безопасности предусмотрены специальные команды (см. ниже раздел «Переходы между режимами безопасности»).

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

Регистры

У каждого из двух режимов безопасности имеются собственные:

  • пара указателей стека MSP и PSP (или единственный указатель MSP в случае процессора без основного расширения и без поддержки ОС);
  • биты SPSEL и nPRIV регистра CONTROL;

Код, выполняемый в безопасном режиме, имеет доступ как к собственным регистрам, так и к регистрам небезопасного режима; код небезопасного режима такого доступа не имеет. Для доступа к внутрипроцессорным регистрам используются обычные команды MSR и MRS. Для доступа к регистрам SCS используются два диапазона адресов: обычный диапазон E000E000–E000EFFF, обеспечивающий обращения к регистрам SCS для текущего режима безопасности, и диапазон E002E000–E002EFFF, дающий коду безопасного режима возможность доступа к SCS небезопасного режима.

Чтобы отличить безопасные и небезопасные версии режимов, в документации используются суффиксы _S и _NS соответственно. Отсутствие суффикса означает версию регистра, соответствующую текущему режиму работы.

Прерывания

Прерывания HardFault, MemManage, UsageFault, SVCall, PendSV и SysTick имеют отдельные обработчики для безопасного и небезопасного режимов.

Прерывания HardFault, BusFault и NMI могут обрабатываться либо только в безопасном режиме, либо в том режиме, в каком они возникли, что определяется состоянием бита AIRCR.BFHFNMINS.

Добавлено прерывание SecureFault, всегда обрабатываемое в безопасном режиме.

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

Для прерываний, обрабатываемых в небезопасном режиме, имеется возможность понижения приоритета, управляемая битом AIRCR.PRIS, при этом значения приоритетов в соответствующих регистрах SCS небезопасного режима остаются прежними.

Переходы между режимами безопасности

Небезопасный код может перейти на выполнение безопасного кода с помощью какой-либо команды перехода, если при этом соблюдаются следующие условия:

  • по адресу перехода размещается команда SG;
  • все байты упомянутой команды находятся в регионе памяти с атрибутами «вызываемый для безопасного и небезопасного кода» (secure & nonsecure callable);
  • выполнение данной команды не вызывает возникновения какого-либо отказа.

Команда SG, размещённая в области безопасного кода, является единственным способом для небезопасного кода вызвать безопасный код.

Безопасный код для перехода в небезопасное состояние должен использовать одну из команд перехода с изменением состояния BXNS или BLXNS, при этом самый младший бит адреса перехода определяет целевое состояние безопасности (0 – небезопасный режим, 1 – безопасный режим).

Если небезопасный код пытается выполнить переход в безопасный регион на любую команду, кроме SG, либо если эта команда не лежит целиком в области, помеченной как вызываемая для безопасного и небезопасного кода, возникает прерывание SecureFault (либо HardFault безопасного режима). То же самое имеет место, если безопасный код выполняет команду перехода с изменением памяти на небезопасный регион, но при этом младший бит адреса перехода равен 1.

Если при последовательном выполнении кода после последней команды в небезопасной области сразу следует первая команда в безопасной области, этой командой является SG и она целиком лежит в области, помеченной как вызываемая для безопасного и небезопасного кода, то в зависимости от реализации может либо произойти переключение в безопасное состояние, либо возникнуть прерывание SecureFault (либо HardFault безопасного режима). Таким образом, полагаться на такой способ перехода к безопасному коду нельзя, надо всегда выполнять явный переход на команду SG.

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

В версии ARMv8.1-M, если команда SG выполняется в режиме небезопасного потока и при этом сама SG выбрана из безопасной памяти, производится загрузка из памяти значения, заданного текущим указателем безопасного стека. Загрузка этого значения производится от имени безопасного режима в признаком привилегированного доступа к шине, установленным в соответствии со значением бита CONTROL_S.nPRIV. Далее, если бит CCR_S.TRD установлен и либо бит CONTROL_S.SPSEL сброшен (безопасный режим использует основной стек), либо старший 31 бит выбранного значения совпадает со старшим 31 битом значения FEFA125A, возникает прерывание SecureFault в связи с недопустимой точкой входа.

Вызов и возврат из функций

Для вызова функции (подпрограммы), которая может принадлежать как безопасному, так и небезопасному коду, безопасный код должен использовать команду BLXNS.

Если вызываемая функция является безопасной, BLXNS будет выполнена идентично обычной команде BLX, т. е. просто сохранит в регистре LR адрес возврата и выполнит переход на заданный адрес. Единственная разница заключается в том, что BLXNS не меняет текущую систему команд, но для процессоров M-профиля это неактуально.

Если же вызываемая функция является небезопасной, команда BLXNS сформирует в текущем стеке (т. е. безопасном стеке обработчика или потока в зависимости от того, какой именно стек в данный момент используется) 8-байтовый кадр и сохранит в нём информацию для возврата, состоящую из двух слов:

  • слово 0 – адрес возврата, т. е. адрес команды, непосредственно следующей за BLXNS;
  • слово 4 – частичный PSR, включающий значение IPSR (номер вектора прерывания, чей обработчик вызывает функцию, либо 0, если вызов исходит из потока) и бит 20, содержащий копию текущего значения бита CONTROL_S.SFPA.

С целью скрыть от небезопасного кода, какой именно обработчик вызвал его, если вызов производится из безопасного обработчика, а не потока, в IPSR после его сохранения в безопасном стеке заносится значение 1. Рекомендуется, чтобы все регистры общего назначения, кроме используемых для передачи параметров вызываемой функции, содержали нули, чтобы скрыть от небезопасного кода потенциально конфиденциальную информацию.

В LR будет занесён код FNC_RETURN, благодаря которому команда, выполняемая для возврата из функции, осуществит возврат к безопасному коду, осуществившему вызов. Этот код имеет следующий формат:

FNC RETURN format





Бит S (младший разряд FNC_RETURN) формально определяет режим безопасности, из которого должен быть выполнен возврат: 0 для безопасного и 1 для небезопасного режима. На практике реально вызов функции с возвратом через FNC_RETURN всегда осуществляется из безопасного кода, а функция при этом является небезопасной (при вызове без изменения режима безопасности всегда используется обычный адрес возврата, а вызов безопасного кода из небезопасного возможен лишь с помощью команды SG по другим правилам, описанным в предыдущем разделе), т. е. при выполнении вызова командой BLXNS данный бит всегда устанавливается. Однако вызванная небезопасная функция может в процессе работы вызывать безопасные функции, используя команду SG, при этом произойдёт сброс младшего бита. По этой причине в процессе возврата из функции, описываемого далее, процессор, обнаружив код FNC_RETURN, игнорирует его младший бит.

Для возврата из функции может использоваться любая из следующих команд:

  • обычная команда BX;
  • любая из команд, осуществляющих загрузку в PC адреса возврата (POP, LDM, LDR);
  • если функция является безопасной, которая потенциально может быть вызвана из небезопасного кода, – команда BXNS.

Эти команды, обнаружив в качестве адреса возврата код FNC_RETURN (равен FEFFFFFFFE для возврата из небезопасного состояния и FEFFFFFF для возврата из безопасного состояния), выполняют следующие действия.

  • Производится определение безопасного стека (основного или процесса), в котором был сохранён кадр с информацией для возврата. Если команда возврата выполняется небезопасным обработчиком (т. е. если текущий IPSR отличен от нуля), то возврат будет выполняться в безопасный обработчик и должен быть использован основной стек; если возврат выполняется небезопасным потоком (IPSR равен нулю), то возврат выполняется в безопасный поток, и используемый стек задаётся битом CONTROL_S.SPSEL.
  • Из выбранного стека извлекается 8-байтовый кадр, содержащий адрес возврата и частичный PSR (см. описанный выше процесс вызова функции).
  • Производится проверка соответствия текущего режима (обработчика или потока) с режимом, заданным в сохранённом PSR. Если последний содержит нуль в битах 8:0, т. е. если вызов выполнялся из безопасного потока, то и текущий IPSR должен содержать нуль (возврат выполняется из потока0. Если Сохранённый PSR содержит ненулевое значение в битах 8:0 (вызов выполнялся из безопасного обработчика), то текущий IPSR должен содержать значение 1 (поскольку при вызове функции в рамках обработчика производится маскировка номера обработчика безопасного режима). При нарушении этих требований возникает прерывание UsageFault, при этом устанавливается бит UFSR.INVPC.
  • Процессор переходит в безопасный режим.
  • Значение указателя стека, содержащего информацию о возврате, увеличивается на 8.
  • В регистр IPSR заносится номер вектора, извлечённый из сохранённого в стеке PSR. Заметим, что если номер вектора не соответствует ни одному вектору, реализованному в данном процессоре, заносимое в IPSR значение не определено.
  • Бит CONTROL_S.SFPA устанавливается равным значению, извлечённому из сохранённого PSR.
  • Поле IT/ICI/ECI в текущем EPSR обнуляется, чтобы избежать влияния небезопасного кода на безопасный.
  • Если имеется LOB, его информация уничтожается.
  • В бит EPSR.T заносится значение младшего бита адреса возврата, извлечённого из стека, чем обеспечивается переключение системы команд.
  • В PC заносится адрес возврата.

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

Материалы сообщества доступны в соответствии с условиями лицензии CC-BY-SA , если не указано иное.