OSDev Wiki
(не показано 7 промежуточных версий этого же участника)
Строка 464: Строка 464:
 
* прерывания MemManage и BusFault (или HardFault, если первые два не поддерживаются), связанные с попыткой сохранения контекста прерванного кода в его стеке;
 
* прерывания MemManage и BusFault (или HardFault, если первые два не поддерживаются), связанные с попыткой сохранения контекста прерванного кода в его стеке;
 
* в процессорах с расширением безопасности и FPU/MVE – прерывание SecureFault или UsageFault, возникающее при анализе состояния FPU перед немедленным выполнением сохранения его контекста или перед активацией [[Обработка прерываний в M-профиле#Отложенное сохранение контекста FPU|отложенного сохранения]];
 
* в процессорах с расширением безопасности и FPU/MVE – прерывание SecureFault или UsageFault, возникающее при анализе состояния FPU перед немедленным выполнением сохранения его контекста или перед активацией [[Обработка прерываний в M-профиле#Отложенное сохранение контекста FPU|отложенного сохранения]];
 
* в процессорах с расширением безопасности – прерывание SecureFault, если прерывание является небезопасным, однако выбранный вектор указывает на безопасную область памяти; при этом устанавливается бит INVEP. Этого, однако, не происходит, если вектор указывает на команду <tt>[[Команда SG (ARM)|SG]]</tt>, находящуюся в безопасной области, допускающей [[Расширение безопасности в M-профиле#Переходы между режимами безопасности|вызов]] из небезопасного кода;
 
* в процессорах с расширением безопасности – прерывание SecureFault, если прерывание является безопасным, а выбранный вектор указывает на небезопасную область памяти; при этом устанавливается бит INVTRAN;
 
* в процессорах версии ARMv8-M – прерывание UsageFault (или HardFault, если UsageFault не поддерживается) по переполнению стека;
 
* в процессорах версии ARMv8-M – прерывание UsageFault (или HardFault, если UsageFault не поддерживается) по переполнению стека;
 
* прерывание BusFault (или HardFault), связанное с попыткой выборки адреса точки входа из таблицы векторов прерываний.
 
* прерывание BusFault (или HardFault), связанное с попыткой выборки адреса точки входа из таблицы векторов прерываний.
Строка 476: Строка 478:
 
Точка наблюдения во время выполнения входа в обработчик может сработать, если она установлена для слежения за областью памяти, в которой выполняется сохранение контекста, или в других подобных случаях. Ошибкой это прерывание не является. Если приоритет обработчика отладочного прерывания (оно отвечает в том числе за обработку событий, связанных с точками наблюдения) ниже или равен приоритету исходного прерывания, то прерывание по точке наблюдения просто игнорируется и управление получает обработчик исходного прерывания. Если же приоритет прерывания по точке наблюдения выше приоритета исходного прерывания, то исходное прерывание получит статус ожидающего, а отладочное прерывание станет активным.
 
Точка наблюдения во время выполнения входа в обработчик может сработать, если она установлена для слежения за областью памяти, в которой выполняется сохранение контекста, или в других подобных случаях. Ошибкой это прерывание не является. Если приоритет обработчика отладочного прерывания (оно отвечает в том числе за обработку событий, связанных с точками наблюдения) ниже или равен приоритету исходного прерывания, то прерывание по точке наблюдения просто игнорируется и управление получает обработчик исходного прерывания. Если же приоритет прерывания по точке наблюдения выше приоритета исходного прерывания, то исходное прерывание получит статус ожидающего, а отладочное прерывание станет активным.
   
Два последних случая указывают на ошибку в процессе входа в обработчик. Ошибкам MemManage и BusFault, связанным с сохранением контекста, будет присвоен класс MSTKERR и STKERR соответственно, что найдёт своё отражение в регистрах состояния прерываний. Прерывание BusFault, связанное с попыткой считывания адреса обработчика из таблицы векторов прерываний, всегда рассматривается как серьёзный отказ и обрабатывается не как BusFault, а как HardFault.
+
Остальные случаи указывают на ошибку в процессе входа в обработчик. Ошибкам MemManage и BusFault, связанным с сохранением контекста, будет присвоен класс MSTKERR и STKERR соответственно, что найдёт своё отражение в регистрах состояния прерываний. Прерывание BusFault, связанное с попыткой считывания адреса обработчика из таблицы векторов прерываний, всегда рассматривается как серьёзный отказ и обрабатывается не как BusFault, а как HardFault.
   
Если приоритет прерывания, порождённого в результате ошибки, ниже или равен приоритету прерванного кода, т. е. если обработчик порождённого прерывания в соответствии с обычными правилами приоритетов немедленно вызван быть не может, произойдёт его повышение до уровня HardFault (приоритет –1). Исходное прерывание получит статус ожидающего и произойдёт вход в обработчик серьёзного отказа.
+
Если приоритет прерывания, порождённого в результате ошибки, ниже или равен приоритету прерванного кода, т. е. если обработчик порождённого прерывания в соответствии с обычными правилами приоритетов немедленно вызван быть не может, произойдёт его [[Обработка прерываний в M-профиле#Повышение приоритета запроса прерывания|повышение]] до уровня HardFault (приоритет –1). Исходное прерывание получит статус ожидающего и произойдёт вход в обработчик серьёзного отказа.
   
Если приоритет порождённого прерывания больше приоритета прерванного процесса, но меньше либо равен приоритету исходного прерывания, порождённое прерывание получит статус ожидающего, после чего произойдёт нормальный вход в обработчик исходного прерывания. При его завершении управление получит обработчик порождённого прерывания. Такой ход событий возможен, поскольку причина ошибки кроется в неверном состоянии стека прерванного кода, что само по себе не препятствует нормальному выполнению обработчиков.
+
Если приоритет порождённого прерывания больше приоритета прерванного процесса, но меньше либо равен приоритету исходного прерывания, порождённое прерывание получит статус ожидающего, после чего произойдёт нормальный вход в обработчик исходного прерывания. При его завершении управление получит обработчик порождённого прерывания. Такой ход событий возможен, поскольку причина ошибки кроется в неверном состоянии стека прерванного кода, что само по себе не препятствует нормальному выполнению обработчика исходного прерывания.
   
 
Наконец, если приоритет порождённого прерывания выше приоритета исходного прерывания, последнее получит статус ожидающего, после чего произойдёт вход в обработчик порождённого прерывания. Обработчик исходного прерывания получит управление лишь после завершения обработчика порождённого прерывания.
 
Наконец, если приоритет порождённого прерывания выше приоритета исходного прерывания, последнее получит статус ожидающего, после чего произойдёт вход в обработчик порождённого прерывания. Обработчик исходного прерывания получит управление лишь после завершения обработчика порождённого прерывания.
   
 
=== Завершение обработчика прерывания ===
   
 
Завершение обработчика прерывания и возврат к прерванному коду происходит, когда в режиме обработчика с помощью одной из команд [[Команда POP (ARM)|<tt>POP</tt>]], [[Команда LDM (ARM)|<tt>LDM</tt>]], [[Команда LDR (ARM)|<tt>LDR</tt>]] или [[Команды BX, BXNS (ARM)|<tt>BX</tt> (<tt>BXNS</tt>)]] в PC загружается значение [[Обработка прерываний в M-профиле#Формат EXC_RETURN|EXC_RETURN]]. Загрузка EXC_RETURN в PC любым другим способом или в режиме потока не является завершением обработчика прерывания, а трактуется как обычный переход.
Если прерывание является небезопасным, однако выбранный вектор указывает на безопасную область памяти, возникает прерывание SecureFault, при этом устанавливается бит INVEP. Этого, однако, не происходит, если вектор указывает на команду <tt>[[Команда SG (ARM)|SG]]</tt>, находящуюся в безопасной области, допускающей [[Расширение безопасности в M-профиле#Переходы между режимами безопасности|вызов]] из небезопасного кода.
 
   
  +
Значение EXC_RETURN в архитектурах ARMv6-M и ARMv7-M идентифицируются по старшим четырём разрядам, в ARMv8-M – по старшим восьми разрядам (все они должны содержать единицы).
Если прерывание является безопасным, а выбранный вектор указывает на небезопасную область памяти, возникает прерывание SecureFault, при этом устанавливается бит INVTRAN.
 
   
  +
В процессе возврата из обработчика прерывания процессоры версий ARMv7-M и ARMv8-M (включая варианты без основного расширения) выполняют различные проверки корректности EXC_RETURN и некоторых других значений. В ARMv6-M никакие проверки корректности не выполняются и осуществляются лишь общие проверки на доступность данных в стеке. Обнаружение какой-либо ошибки вызывает формирование запроса соответствующего прерывания. Обработка таких запросов будет описана отдельно.
=== Завершение обработчика прерывания ===
 
   
  +
При нарушении любого из следующих требований к содержимому EXC_RETURN результаты будут непредсказуемы (в частности, процессор может не выполнять проверку указанных разрядов и, соответственно, выполнять возврат из прерывания, как если бы эти разряды имели требуемые значения):
Завершение обработчика прерывания и возврат к прерванному коду происходит, когда в режиме обработчика с помощью одной из команд [[Команда POP (ARM)|<tt>POP</tt>]], [[Команда LDM (ARM)|<tt>LDM</tt>]], [[Команда LDR (ARM)|<tt>LDR</tt>]] или [[Команды BX, BXNS (ARM)|<tt>BX</tt> (<tt>BXNS</tt>)]] в PC загружается значение [[Обработка прерываний в M-профиле#Формат EXC_RETURN|EXC_RETURN]]. Загрузка EXC_RETURN в PC любым другим способом или в режиме потока не является завершением обработчика прерывания, а трактуется как обычный переход.
 
  +
  +
* биты 23:7 в ARMv8-M и 27:5 в ARMv7-M должны содержать единицы;
  +
  +
* бит 1 в ARMv8-M должен быть равен нулю;
  +
  +
* если процессор не поддерживает FPU/MVE, бит 4 (FType) должен быть единичным;
  +
 
* в ARMv8-M при отсутствии [[Расширение безопасности в M-профиле|расширения безопасности]] биты 6 (S) и 0 (ES) должны быть равны нулю, а бит 5 (DCRS) – единице; в ARMv7-M все три перечисленных бита должны быть единичными. Таким образом, значение EXC_RETURN, программно сформированное для архитектуры ARMv6-M или ARMv7-M, не является полностью совместимым со значением EXC_RETURN для ARMv8-M без расширения безопасности (будет содержать единицу в бите 0).
  +
  +
Архитектура не определяет конкретный порядок доступов к памяти во время восстановления контекста прерванного кода, задаётся лишь описываемый далее общий набор действий, которые должны быть выполнены в ходе завершения обработчика прерывания.
  +
  +
* В ARMv8-M с расширением безопасности проверяется, не пытается ли обработчик, фактически выполняющийся в небезопасном режиме, выполнить завершение с установленным битом EXC_RETURN.ES (ложно утверждая тем самым, что он является безопасным). При обнаружении этого нарушения:
  +
  +
:* бит EXC_RETURN.ES принудительно сбрасывается;
  +
  +
:* сбрасывается бит активности завершающегося прерывания (номер прерывания содержится в регистре [[Регистры состояния и управления в M-профиле#Регистры состояния|IPSR]]) для режима безопасности, заданного битом EXC_RETURN.ES (в данном случае это небезопасный режим);
  +
 
:* бит [[Регистры состояния и управления в M-профиле#Регистр CONTROL|CONTROL]].SPSEL, соответствующий режиму безопасности, определяемому битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL, чем восстанавливается выбор стека, имевший место до входа в завершающийся обработчик прерывания;
  +
  +
:* процесс возврата из прерывания прекращается и вместо этого формируется запрос прерывания SecureFault с установкой бита [[Блок управления системой в M-профиле#Регистр состояния прерывания SecureFault SFSR|SFSR]].INVER (в процессоре без основного расширения прерывание SecureFault отсутствует, поэтому запрашивается прерывание HardFault). При входе в обработчик, вызываемый сразу после этого, состояние бита EXC_RETURN.DCRS в регистре LR будет неопределённым (хотя сама ARM рекомендует, чтобы этот бит имел какое-либо определённое значение).
  +
 
:: Заметим, что при завершении обработчика, реально выполняющегося в безопасном режиме, состояние бита EXC_RETURN.ES не проверяется, поэтому такой обработчик может завершить обработку прерывания, утверждая, что он является небезопасным.
  +
  +
* В ARMv8-M с расширением безопасности, если сброшены оба бита EXC_RETURN.ES и EXC_RETURN.DSRS, выполняются те же действия по обработке ошибки, что описаны выше (за исключением сброса бита EXC_RETURN.ES, который и так сброшен).
  +
  +
* Производится проверка, установлен ли бит активности для прерывания, чей обработчик завершается. Для выбора соответствующего регистра [[Блок управления системой в M-профиле#Регистр состояния и управления системными обработчиками SHCSR|SHCSR]] или [[Контроллер прерываний NVIC#Регистры активности прерываний NVIC_IABR|NVIC_IABR]] используются номер прерывания (находится в регистре IPSR) и режим безопасности, определяемый битом EXC_RETURN.ES. Если бит активности сброшен, выполняются следующие действия по обработке этой ошибки:
  +
  +
:* бит CONTROL.SPSEL, соответствующий режиму безопасности, определяемому битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL;
  +
  +
:* процесс возврата из прерывания прекращается и вместо этого формируется запрос прерывания UsageFault для режима безопасности, заданного битом EXC_RETURN.ES, с установкой бита [[Блок управления системой в M-профиле#Регистр состояния прерывания UsageFault UFSR|UFSR]].INVPC (в ARMv8-M без основного расширения прерывание UsageFault отсутствует, поэтому запрашивается прерывание HardFault).
  +
 
* В версии ARMv7-M, если бит [[Блок управления системой в M-профиле#Регистр управления и конфигурации CCR|CCR]].NONBASETHRDENA сброшен и выполняется возврат в режим потока, проверяется, нет ли активных прерываний, за исключением завершаемого в данный момент. При нарушении этого требования выполняются описанные в предыдущем пункте действия по обработке ошибки.
  +
  +
:: Заметим, что в ARMv8-M бит CCR.NONBASETHRDENA формально отсутствует (фактически он зафиксирован в установленном состоянии), поэтому данная проверка не выполняется, что разрешает выполнять возврат в режим потока при наличии активных прерываний.
  +
 
* В версии ARMv7-M проверяются биты EXC_RETURN[3:0]: они могут быть равны лишь одной из разрешённых комбинаций (1, 9 или D). При нарушении этого выполняются описыванные выше действия по обработке ошибки.
  +
  +
* Бит CONTROL.SPSEL, соответствующий режиму безопасности, заданному битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL.
  +
 
* Производится сброс бита активности завершающегося прерывания (номер прерывания содержится в IPSR) для заданного битом EXC_RETURN.ES режима безопасности.
  +
 
* В процессорах версии ARMv8-M с FPU/MVE и расширением безопасности, если одновременно установлены биты CONTROL.FPCA (завершающийся обработчик изменил регистры FPU) и [[Регистры управления FPU в M-профиле#Регистр управления контекстом FPCCR|FPCCR]].CLRONRET (требуется очистка регистров FPU при завершении обработчика прерывания):
  +
  +
:* если установлен бит FPCCR_S.LSPACT (запрошено отложенное сохранение безопасного контекста FPU; в сочетании с установленным битом CONTROL.FPCA это означает, что контекст прерванного безопасного кода должен был быть сохранён, но фактически сохранён не был и поэтому оказался разрушен), формируется запрос прерывания SecureFault с установкой бита SFSR.LSERR; дальнейшие действия по возврату из обработчика не выполняются;
  +
  +
:* если версия архитектуры – ARMv8.1-M и завершающийся обработчик не имеет доступа к FPU (с учётом режима безопасности, указанного битом EXC_RETURN.ES), устанавливается бит USFR.NOCP и запрашивается прерывание UsageFault. Режим безопасности для запрашиваемого прерывания определяется причиной, блокирующей доступ к FPU: если доступ заблокирован регистром [[Блок управления системой в M-профиле#Регистр управления доступом к сопроцессорам CPACR|CPACR]] режима безопасности, заданного битом EXC_RETURN.ES, прерывание адресуется этому же режиму безопасности; если доступ блокирует регистр [[Блок управления системой в M-профиле#Регистр управления небезопасными доступами NSACR|NSACR]], прерывание адресуется безопасному режиму; если причиной блокировки является регистр [[Прочие регистры SCS в M-профиле#Регистр управления питанием сопроцессоров CPPWR|CPPWR]] – прерывание адресуется безопасному режиму, если установлен бит SUS10 этого регистра, и режиму, заданному битом EXC_RETURN.ES, в противном случае. Проверки на блокировку осуществляются в перечисленном порядке. Дальнейшие действия по возврату из обработчика не выполняются;
  +
  +
:* регистры FPU S0–S15 (но не S16–S31), FPSCR и VPR обнуляются.
  +
  +
* Если поддерживается [[Обработка прерываний в M-профиле#Сцепление прерываний|сцепление прерываний]] и имеется какой-либо запрос прерывания, который можно обслужить немедленно, производится вызов обработчика этого прерывания без восстановления контекста прерванного ранее кода. Если же пригодных для немедленного обслуживания запросов нет или сцепление не поддерживается выполняются описываемые действия по восстановлению контекста прерванного кода.
  +
  +
:: Заметим, что сцепление прерываний в процессе возврата из обработчика ранее данного момента (т. е. выполнения перечисленных выше проверок, а также восстановления состояния бита CONTROL.SPSEL, сброса бита активности завершающегося прерывания и, возможно, обнуления регистров FPU) невозможно, однако оно может произойти не только в этой, но и в любой последующей точке возврата из обработчика. При этом, если процессор с расширением безопасности уже успел полностью или частично восстановить безопасный контекст, производится обнуление восстановленных регистров (если процессор не имеет расширения безопасности, эти регистры могут быть в произвольном состоянии).
   
  +
* Устанавливается режим безопасности, в который выполняется возврат (определяется значением бита EXC_RETURN.S).
Значение EXC_RETURN в архитектурах ARMv6-M и ARMv7-M идентифицируются по старшим четырём разрядам, в ARMv8-M – по старшим восьми разрядам. Если биты 23:7 в ARMv8-M и 27:5 в ARMv7-M и ARMv6-M не являются единицами, последствия будут непредсказуемыми; это же относится к ненулевому значению бита 1 в ARMv8-M. Если процессор не поддерживает FPU/MVE, бит 4 (FType) должен быть единичным. При отсутствии в ARMv8-M [[Расширение безопасности в M-профиле|расширения безопасности]] биты 6 (S) и 0 (ES) должны быть равны нулю, а бит 5 (DCRS) – единице; в предыдущих версиях архитектуры все три перечисленных бита должны быть единичными. Таким образом, значение EXC_RETURN, программно сформированное для архитектуры ARMv6-M или ARMv7-M, не является полностью совместимым со значением для ARMv8-M даже при отсутствии у процессора расширения безопасности.
 
   
  +
* Если установлены биты EXC_RETURN.Mode (возврат в режим потока) и [[Блок управления системой в M-профиле#Регистр управления системой SCR|SCR]].SLEEPONEXIT и при этом активных прерываний больше нет, процессор переходит в [[Энергосбережение в M-профиле#Сон при выходе из обработчиков прерываний|состояние сна]]. Не совсем ясно, что произойдёт при выходе из сна при поступлении прерывания: либо восстановление контекста (которое на момент входа в сон выполнено ещё не было) с последующим прерыванием, либо сцепление прерываний. С практической точки зрения это влияет лишь на задержку вызова обработчика поступившего прерывания, но логично предположить, что, если сцепление возможно, произойдёт именно оно.
При завершении обработчика процессор выполняет ряд проверок корректности возврата. Проверки осуществляются в порядке их перечисления; при обнаружении ошибки последующие проверки не выполняются.
 
   
  +
* Производится восстановление регистров, сохранённых в стеке прерванного кода. Архитектура не определяет порядок восстановления регистров, а лишь общий набор выполняемых для этого действий. Любой отказ, вызванный попыткой считывания значения из стека, прекращает дальнейшую выборку данных из стека, а соответственно, восстановление контекста как таковое, и приводят к формированию соответствующего запроса прерывания (который, однако, в зависимости от реализации может быть потерян, если одновременно обнаруживается неверное состояние отложенного сохранения безопасного контекста FPU, о чём будет сказано ниже), но не отменяет других действий по возврату из обработчика. Такое поведение связано с тем, что невозможность корректного восстановления контекста прерванного кода препятствует возобновлению выполнения этого кода (в связи с чем и формируется запрос прерывания MemManage или BusFault), но не мешает завершить корректным образом обработчик прерывания – т. е. логически возникшее прерывание относится к прерванному коду, к которому выполняется возврат, а не к завершившемуся обработчику.
* Если имеется расширение безопасности, процессор на момент завершения обработчика находится в небезопасном режиме, однако бит EXC_RETURN.ES установлен (ложно утверждая тем самым, что завершающийся обработчик является безопасным), формируется запрос прерывания SecureFault с установкой бита [[Блок управления системой в M-профиле#Регистр состояния прерывания SecureFault SFSR|SFSR]].INVER (в процессоре без основного расширения возникает прерывание HardFault), при этом бит EXC_RETURN.ES сбрасывается.
 
   
  +
:* Определяются режимы доступа к памяти (соответствуют режимам прерванного кода, к которому осуществляется возврат) и выбирается стек, в котором находится подлежащий восстановлению контекст. Если при этом выбранный SP не кратен 8, результат будет непредсказуем.
:: Заметим, что в безопасном режиме состояние бита EXC_RETURN.ES не проверяется, поэтому обработчик, фактически выполняющийся в безопасном режиме, может завершить обработку прерывания, утверждая, что он является небезопасным.
 
   
  +
:* В ARMv8-M с расширением безопасности, если был сохранён дополнительный контекст процессора (т. е. если бит EXC_RETURN.S установлен, а любой из битов EXC_RETURN.ES и EXC_RETURN.DCRS сброшен):
* Если имеется расширение безопасности и сброшены оба бита EXC_RETURN.ES и EXC_RETURN.DSRS, запрашивается SecureFault и устанавливается SFSR.INVER.
 
   
  +
::* производится проверка сигнатуры целостности, которая была помещена в самое нижнее слово кадра стека, выделенного при входе в обработчик прерывания: она должна быть равна FEFA125A, если бит EXC_RETURN.FType сброшен, и FEFA125B, если он установлен. При обнаружении неверной сигнатуры формируется запрос прерывания SecureFault с установкой бита SFSR.INVIS и возврат из обработчика прекращается;
* Текущее прерывание (его номер находится в регистре [[Регистры состояния и управления в M-профиле#Регистры состояния|IPSR]]) должно быть отмечено как активное в соответствующем регистре [[Блок управления системой в M-профиле#Регистр состояния и управления системными обработчиками SHCSR|SHCSR]] или [[Контроллер прерываний NVIC#Регистры активности прерываний NVIC_IABR|IABR]] (с учётом режима безопасности, из которого фактически выполняется возврат). Если это не так, то формируется запрос прерывания UsageFault с установкой бита [[Блок управления системой в M-профиле#Регистр состояния прерывания UsageFault UFSR|UFSR]].INVPC (в процессоре без основного расширения возникает прерывание HardFault). Если процессор при этом имеет расширение безопасности, данное прерывание адресуется тому же режиму безопасности, в котором выполнялся завершающийся обработчик.
 
   
  +
::* выполняется восстановление дополнительного контекста процессора (регистров R4–R11).
* Если возврат из обработчика происходит в режим потока, значение IPSR, восстанавливаемое из стека, должно быть равно нулю; если же возврат происходит в другой обработчик, это значение должно быть отлично от нуля. В случае нарушения предпринимаются те же действия, что и в предыдущем случае.
 
   
  +
:* Восстанавливаются регистры основного контекста процессора (R0–R3, R12, LR, PC и PSR). При восстановлении двух последних регистров выполняются дополнительные действия:
* В версии ARMv7-M, если бит [[Блок управления системой в M-профиле#Регистр управления и конфигурации CCR|CCR]].NONBASETHRDENA сброшен и выполняется возврат в режим потока, проверяется, нет ли активных прерываний, за исключением завершаемого в данный момент. При обнаружении других активных прерываний формируется запрос прерывания UsageFault с установкой бита UFSR.INVPC. В ARMv8-M данный бит зафиксирован в установленном состоянии, поэтому данная проверка не выполняется.
 
   
  +
::* считанные из стека разряды PC[31:1] помещаются в соответствующий регистр процессора; бит 0 восстановленного значения игнорируется;
* В версии ARMv7-M проверяются биты EXC_RETURN[3:0]: они могут быть равны лишь одной из разрешённых комбинаций (1, 9 или D). При нарушении этого требования возникает прерывание UsageFault с установкой бита UFSR.INVPC.
 
   
  +
::* производится проверка соответствия значения IPSR, восстановленного из стека (биты PSR[8:0]), режиму выполнения кода, к которому производится возврат (задаётся битом EXC_RETURN.Mode). Если происходит возврат в режим потока, а IPSR отличен от нуля, либо возврат в режим обработчика, а IPSR равен нулю, формируется запрос прерывания UsageFault с установкой бита UFSR.INVPC и процесс возврата из прерывания прекращается;
* На процессорах с FPU/MVE и расширением безопасности, если одновременно установлены биты CONTROL.FPCA (завершающийся обработчик изменил регистры FPU) и FPCCR_S.LSPACT (запрошено отложенное сохранение контекста FPU; в сочетании с предыдущим это означает, что контекст прерванного кода должен был, но фактически не был сохранён и поэтому разрушен), возникает прерывание SecureFault с установкой бита SFSR.LSERR.
 
   
  +
::* если восстановленное значение IPSR проходит описанную выше проверку, но при этом не соответствует ни одному имеющемуся в данной реализации прерыванию, реальное значение, помещённое в IPSR, является непредсказуемым;
* На процессорах версии ARMv8.1-M с FPU/MVE и расширением безопасности, если одновременно установлены биты CONTROL.FPCA и [[Регистры управления FPU в M-профиле#Регистр управления контекстом FPCCR|FPCCR]].CLRONRET (требуется очистка регистров FPU при завершении обработчика прерывания), проверяется, имеет ли завершающийся обработчик доступ к FPU (с учётом режима безопасности, указанного битом EXC_RETURN.ES). Если доступа нет, возникает прерывание UsageFault соответствующего режима безопасности с установкой бита USFR.NOCP.
 
   
  +
::* бит 9 восстановленного значения PSR будет использоваться позже при увеличении указателя стека, чтобы при необходимости отменить выполненное при входе в обработчик прерывания принудительное выравнивание стека на границу 8 байтов;
В версии ARMv6-M проверки не выполняются, а нарушение правил возврата из обработчика имеет непредсказуемые последствия.
 
   
  +
::* в процессоре с расширением безопасности значение бита 20 восстановленного значения PSR будет позже использоваться для установки бита CONTROL_S.SFPA;
После проверок выполняются действия по возврату из прерывания.
 
   
  +
::* остальные значащие биты PSR, восстановленные из стека, заносятся в соответствующие разряды регистра PSR процессора.
* Производится сброс бита активности завершающегося прерывания (номер прерывания содержится в IPSR) для заданного битом EXC_RETURN.ES (с учётом возможного сброса последнего при проверке) режима безопасности.
 
   
  +
:* Если процессор имеет FPU/MVE:
* Бит [[Регистры состояния и управления в M-профиле#Регистр CONTROL|CONTROL]].SPSEL, соответствующий режиму безопасности, заданному битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL.
 
   
  +
::* если бит EXC_RETURN.FType сброшен (прерванный код изменял регистры FPU, поэтому они должны быть восстановлены):
* Если была обнаружена любая из перечисленных выше ошибок, дальнейшие действия по возврату из прерывания не выполняются; вместо этого немедленно начинается вход в обработчик запрошенного в результате проверки прерывания (или в HardFault, если запрошенное прерывание SecureFault или UsageFault запрещено или имеет недостаточный приоритет).
 
   
  +
:::* если в ARMv8-M с расширением безопасности выполняется возврат в небезопасный режим (бит EXC_RETURN.S сброшен) и бит FPCCR_S.LSPACT установлен (было запланировано, но не было выполнено сохранение безопасного контекста FPU), формируется запрос прерывания SecureFault с установкой бита SFSR.LSERR. Если при этом ранее возник отказ при доступе к стеку для выборки сохранённых значений основного или дополнительного контекста процессора, то в зависимости от реализации запрос прерывания MemManage/BusFault может сохраняться вместе с SecureFault или может быть сохранён только SecureFault;
* На процессорах с FPU/MVE и расширением безопасности, если одновременно установлены биты CONTROL.FPCA и FPCCR.CLRONRET, обнуляются регистры FPU S0–S15 (но не S16–S31), FPSCR и VPR.
 
   
  +
:::* если предыдущая проверка была успешна, проверяется, не активен ли запрос на отложенное сохранение для режима безопасности, в который выполняется возврат (т. е. проверяется состояние бита FPCCR.LSPACT для уже установленного по значению бита EXC_RETURN.S режима безопасности). Если этот бит установлен, отложенное сохранение не выполнялось, т. е. регистры FPU по-прежнему содержат контекст прерванного кода; в такой ситуации бит FPCCR.LSPACT сбрасывается и никаких других действий с контекстом FPU не выполняется;
*
 
   
  +
:::* если же бит FPCCR.LSPACT был сброшен (что вместе со сброшенным EXC_RETURN.FType означает, что контекст FPU был фактически сохранён в стеке), проверяется доступность FPU (CP10) для кода, к которому выполняется возврат (с учётом его режимов безопасности и привилегированности – последний определяется по состоянию бита CONTROL.nPRIV для текущего режима безопасности, ранее установленного по значению бита EXC_RETURN.S). Если FPU недоступен, запрашивается прерывание UsageFault с установкой бита UFSR.NOCP и восстановление контекста FPU не выполняется, хотя сам процесс завершения обработчика прерывания продолжается. Если же FPU доступе, производится восстановление его регистров S0–S15, FPSCR и VPR (последний имеется только в процессорах с MVE). Кроме того, если производится возврат в безопасный режим (EXC_RETURN.S установлен) и бит FPCCR_S.TS установлен (контекст FPU считается безопасным), производится восстановление дополнительного контекста FPU (регистры S16–S31). Если в процессе восстановления контекста FPU возник отказ, то при наличии расширения безопасности, восстановленные регистры обнуляются, а при его отсутствии значения регистров являются неопределёнными.
   
  +
::* Бит CONTROL.FPCA устанавливается равным инверсному значению бита EXC_RETURN.FType.
==== Возникновение прерывания при выходе из обработчика прерывания ====
 
   
  +
:* Если на предыдущих шагах не возникло прерываний по ошибкам, значение SP увеличивается на размер сохранённого контекста (с учётом возможного принудительного выравнивания SP, что определяется битом 9 восстановленного значения PSR).
Прерывание может возникнуть и во время выполнения процедуры возврата из обработчика. Как и в случае со входом в обработчик, его причиной может послужить внешнее асинхронное прерывание, срабатывание точки наблюдения либо какая-либо ошибка, связанная с попыткой восстановления контекста.
 
   
  +
:* В процессоре с расширением безопасности бит CONTROL_S.SFPA устанавливается равным 20-му биту восстановленного из стека значения PSR.
В случае возникновения внешнего прерывания, чей приоритет выше, чем приоритет выполнения кода, к которому происходит возврат, вместо возврата имеет место сцепление прерываний, в результате чего управление получит обработчик нового прерывания.
 
   
  +
* Если никаких отказов в процессе восстановления регистров обнаружено не было, выполняются завершающие действия по выходу из прерывания:
Прерывание по точке наблюдения будет проигнорировано, если его приоритет ниже либо равен приоритету кода, к которому производится возврат; в противном случае произойдёт сцепление прерываний и управление получит обработчик отладочного прерывания.
 
   
  +
:* очищается монитор монопольного доступа к памяти, используемый командами типа [[Команда LDREX (ARM)|<tt>LDREX</tt>]] и [[Команда STREX (ARM)|<tt>STREX</tt>]];
Для прерываний MemManage и BusFault, связанных с попыткой восстановления контекста, в регистрах состояния прерываний будет установлен класс MUNSTKERR и UNSTKERR соответственно. Если приоритет соответствующего обработчика выше, чем приоритет кода, к которому осуществляется возврат, произойдёт сцепление прерываний и управление получит обработчик ошибки. Если же его приоритет ниже или равен приоритету кода, к которому выполняется возврат, произойдёт повышение приоритета до уровня HardFault (–1), после чего с помощью сцепления прерываний будет вызван обработчик серьёзного отказа.
 
  +
:* устанавливается [[Энергосбережение в M-профиле#Ожидание событий|регистр события]];
  +
:* выполняется [[Синхронизация изменения состояния в M-профиле|барьер синхронизации команд]].
   
 
=== Сцепление прерываний ===
 
=== Сцепление прерываний ===

Версия от 00:42, 1 февраля 2020

Общие сведения

Обработка прерываний в M-профиле кардинально отличается от таковой в других профилях архитектуры ARM.

Документация на M-профиль различает несколько видов прерываний.

  • Сброс (reset) является особым видом прерывания, немедленно прекращающим выполнение текущей операции и переводящим процессор в предопределённое состояние без возможности возвращения управления ранее выполнявшемуся коду.
  • Синхронный вызов супервизора (SVCall) выполняется по команде SVC.
  • Асинхронный вызов супервизора (PendSV) выполняется с помощью механизма отложенного вызова супервизора через регистр ICSR.
  • Отказ (fault) возникает при обнаружении какой-либо ошибки при выборке или выполнении команды. Отказы могут быть синхронными (прямо связаны с определённой командой) или асинхронными (вызываться какой-либо из ранее выполненных команд, если ошибка не могла быть выявлена сразу). Асинхронным в M-профиле является лишь неточный отказ шины (imprecise BusFault), возникающий при неудачной попытке записи в память. Процессор ради увеличения производительности может не дожидаться фактического завершения записи и продолжать выполнять команды дальше, поэтому при позднем обнаружении отказа установить, какая именно команда стала причиной ошибки, и выполнить «откат» к ней в общем случае невозможно.
К отказам технически относятся также синхронные события, связанные с отладкой (асинхронные отладочные события обрабатываются как внешние прерывания).
  • Внешнее прерывание (interrupt) вызывается внешним по отношению к программе событием, например, изменением состояния какой-либо из входных линий микроконтроллера или завершением передачи байта через последовательный интерфейс. Внешние прерывания возникают асинхронно по отношению к выполняемому потоку команд. Технически к ним также относятся асинхронные отладочные события.

Каждое прерывание имеет следующие характеристики:

  • номер прерывания;
  • приоритет;
  • вектор в памяти, где хранится адрес точки входа в обработчик прерывания.

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

  • неактивно (inactive) — прерывание не запрошено и не обслуживается;
  • ожидает (pending) — прерывание запрошено, но его обработка ещё не начата;
  • активно (active) — прерывание обрабатывается в данный момент либо его обработка была начата, но в настоящее время прервана для обработки более приоритетного прерывания;
  • активно и ожидает (active and pending) — обработка прерывания была начата, однако запрос на это же прерывание продолжает присутствовать и ожидать обслуживания. В таком состоянии могут находиться лишь асинхронные прерывания.

При поступлении запроса прерывания, если при этом не возникает блокировка, производится установка бита состояния ожидания прерывания в соответствующем регистре, а также при необходимости фиксируется связанная с запросом информация (например, причина возникновения данного прерывания, если таковых может быть несколько).

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

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

Примечание. «Существенность» превосходства приоритета запроса прерывания над приоритетом выполнения определяется механизмом группирования приоритетов.

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

Номера и векторы прерываний

Таблица векторов прерываний имеет размер N+1 слов, где N — число различных прерываний, поддерживаемых данной реализацией архитектуры. По смещению 0 относительно начала таблицы находится значение, загружаемое в указатель основного стека MSP при сбросе (если процессор поддерживает расширение безопасности, загрузка производится в MSP безопасного режима). За ним располагаются собственно векторы, т. е. адреса точек входа в соответствующие обработчики прерываний (заметим, что младший бит адреса точки входа всегда должен быть равен единице, задавая использование системы команд Thumb). Для прерывания с номером N вектор в таблице располагается по смещению 4*N. Базовый адрес таблицы векторов в памяти находится в регистре VTOR.

Архитектурой определены следующие стандартные номера прерываний:

  • Сброс (reset) — номер 1. Имеет высший приоритет, численно равный –3 (ARMv6-M и ARMv7-M) или –4 (ARMv8-M) и не может быть запрещён. Возобновление выполнения прерванного сбросом кода невозможно. Хотя формально сброс отнесён к прерываниям, реально таковым он не является; по этой причине описание выполняемых по нему действий вынесено в отдельный раздел.
  • Немаскируемое прерывание (NMI, Non Maskable Interrupt) — номер 2. Имеет фиксированный приоритет –2 (наибольший, не считая сброса и, в некоторых случаях, прерывания HardFault, см. ниже) и не может быть запрещено. Немаскируемые прерывания могут запрашиваться аппаратно либо программно через регистр ICSR.
  • Серьёзный отказ (HardFault) — номер 3. Не может быть запрещён и обычно имеет приоритет –1 (третий после сброса и NMI).
Если имеется расширение безопасности, то при установленном бите BFHFNMINS регистра AIRCR серьёзный отказ в безопасном режиме имеет приоритет –3 (самый высокий, не считая сброса). При сброшенном бите BFHFNMINS, а также в небезопасном режиме серьёзный отказ имеет приоритет –1. Серьёзные отказы могут обрабатываться независимо для каждого из режимов безопасности или же вся обработка может выполняться только обработчиком безопасного режима.
Серьёзный отказ фиксируется каждый раз, когда невозможно вызвать «родной» обработчик возникшего синхронного прерывания, и обычно является признаком невосстановимой ошибки. В частности, серьёзный отказ возникает, если синхронное прерывание происходит в момент, когда его обработка запрещена.
В архитектурах ARMv8-M без основного расширения и ARMv6-M многие другие прерывания отсутствуют, и вместо них сразу возникает серьёзный отказ.
  • Управление памятью (MemManage) — номер 4. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание происходит, если программа обращается к той или иной ячейке памяти недопустимым образом (нарушает права доступа либо стандартные ограничения на доступ к определённым областям адресного пространства).
Это прерывание может быть запрошено также путём установки соответствующего бита в регистре SHCSR. Такая возможность имеется у отладчика, однако доступность этого регистра для программы определяется версией архитектуры и конкретной реализацией.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание отсутствует, вместо него возникает серьёзный отказ.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Отказ шины (BusFault) — номер 5. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание возникает при ошибках во время попытки доступа к памяти, не связанных с нарушением прав доступа и иных чётко определённых правил, приводящим к возникновению прерывания MemManage. Например, к отказу шины может привести ошибка чётности в считанной из памяти информации. Прерывания этого типа могут быть как синхронными, так и асинхронными.
Это прерывание может быть запрошено также путём установки соответствующего бита в регистре SHCSR. Такая возможность имеется у отладчика, однако доступность этого регистра для программы определяется версией архитектуры и конкретной реализацией.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание отсутствует, вместо него возникает серьёзный отказ.
  • Ошибка использования (UsageFault) — номер 6. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание связано с разного рода ошибками в программе:
  • недопустимым кодом команды или попыткой выполнить привилегированную команду в непривилегированном режиме;
  • неверным состоянием выполнения (например, попыткой перейти на систему команд ARM, которая в M-профиле не поддерживается);
  • ошибкой, возникшей при возврате из обработчика прерывания;
  • попыткой доступа к отключённому или отсутствующему сопроцессору;
  • попыткой невыровненного доступа, если включён контроль выравнивания;
  • попыткой выполнить деление на 0.
Это прерывание может быть запрошено также путём установки соответствующего бита в регистре SHCSR. Такая возможность имеется у отладчика, однако доступность этого регистра для программы определяется версией архитектуры и конкретной реализацией.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание отсутствует, вместо него возникает серьёзный отказ.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Нарушение безопасности (SecureFault) – номер 7. Приоритет этого прерывания настраивается и оно может быть запрещено, однако его возникновение в запрещённом состоянии приведёт к серьёзному отказу. Это прерывание имеется лишь в процессорах, поддерживающих расширение безопасности.
  • Прерывания с номерами 8—10 зарезервированы.
  • Вызов супервизора (SVCall) — номер 11. Это прерывание имеет настраиваемый приоритет и происходит при выполнении команды SVC, а поэтому не может быть запрещено.
Документация на архитектуру ARMv6-M, перечисляя прерывания, не упоминает о том, что данное прерывание (вместе с вызывающей его командой SVC) может отсутствовать. Однако исходные тексты ядра Cortex-M1 подтверждают, что данное прерывание имеется лишь в случае, если при сборке ядра выбрана поддержка ОС. То же самое относится к версии ARMv8-M без основного расширения.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Отладочный монитор (Debug monitor) — номер 12. Приоритет этого прерывания настраивается. В общем случае прерывания, генерируемые отладочным монитором, являются синхронными и обрабатываются как отказы, однако точки слежения (debug watchpoints) порождают асинхронные события, обрабатываемые аналогично внешним прерываниям.
В версиях ARMv8-M без основного расширения и ARMv6-M это прерывание возникать не может.
  • Прерывание с номером 13 зарезервировано.
  • Отложенный вызов супервизора (PendSV) — номер 14. Это прерывание имеет настраиваемый приоритет и всегда разрешено. Оно управляется с помощью регистра ICSR и происходит асинхронно по отношению к выполняемому программному коду.
Документация на архитектуру ARMv6-M, перечисляя прерывания, не упоминает о том, что данное прерывание может отсутствовать. Однако исходные тексты ядра Cortex-M1 подтверждают, что данное прерывание имеется лишь в случае, если при сборке ядра выбрана поддержка ОС. То же самое относится к версии ARMv8-M без основного расширения.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров.
  • Системный таймер (SysTick) — номер 15. Это прерывание с настраиваемым приоритетом всегда разрешено. Его аппаратная генерация может быть запрещена, однако его всегда можно вызвать программно через регистр ICSR. Оно связано со стандартным таймером, входящим в процессоры M-профиля, и является асинхронным.
В версии ARMv6-M это прерывание является необязательным (отсутствует, если в конкретной реализации нет системного таймера). В частности, оно отсутствует в ядре Cortex-M1, если при сборке последнего поддержка ОС не включена. То же самое относится к версии ARMv8-M без основного расширения.
При наличии расширения безопасности для каждого режима безопасности имеется свой обработчик этого прерывания и свой набор управляющих регистров (при условии, что для данного режима безопасности таймер вообще имеется).
  • Внешние прерывания (external interrupts). Нумерация источников этих прерываний является двоякой: номера отведённых для них векторов, а соответственно, и номера прерываний с точки зрения процессора, начинаются с 16, однако номера источников запросов, поступающих в контроллер прерываний NVIC, начинаются с нуля (это объясняется тем, что логически NVIC работает исключительно с внешними прерываниями, в то время как все описанные выше прерывания обрабатываются непосредственно процессором). Эти прерывания имеют настраиваемый приоритет и могут запрещаться. Все они являются асинхронными.
Максимальный номер прерывания определяется реализацией, но не может превышать 511, что ограничивает количество возможных внешних прерываний 496-ю. В версии ARMv6-M может быть реализовано не более 32 внешних прерываний, однако в ARMv8-M их может быть 496 независимо от наличия основного расширения.

Приоритеты прерываний

Приоритет прерывания тем выше, чем алгебраически меньше обозначающее его число. Три самых приоритетных прерывания — сброс, NMI и серьёзный отказ — имеют фиксированные приоритеты, выражаемые отрицательными числами. Все остальные прерывания имеют программно назначаемые приоритеты (стандартные прерывания – через регистры SHPR1–SHPR3, внешние прерывания – через регистры IPR), причём наименьшее значение, а соответственно, наибольший приоритет будет равен нулю. Благодаря последнему обстоятельству все три прерывания с фиксированными приоритетами всегда являются более приоритетными, чем любые другие прерывания.

Когда одновременно возникает несколько прерываний с разными приоритетами, управление получит обработчик прерывания с наибольшим (численно наименьшим) приоритетом. Если имеется несколько запросов с одинаковым приоритетом, первым будет обслужено прерывание с наименьшим номером.

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

Сброс устанавливает все программно настраиваемые приоритеты в нуль.

Программируемые приоритеты

Как уже отмечалось, почти все прерывания имеют программируемый приоритет. В версиях ARMv8-M с основным расширением и ARMv7-M эта величина в зависимости от реализации занимает от 3 до 8 разрядов; в версиях ARMv8-M без основного расширения и ARMv6-M – всегда два бита. Для обеспечения максимальной программной совместимости реализованные биты приоритета всегда являются старшими разрядами байта, при этом младшие разряды, соответствующие нереализованным битам, считаются равными нулю. Таким образом, минимальный приоритет прерывания в зависимости от реализованного числа разрядов может быть равен 192, 224, 240, 248, 252, 254 или 255 (C0, E0, F0, F8, FC, FE или FF); максимальный программируемый приоритет всегда равен нулю. Запись единиц в нереализованные разряды игнорируется; при чтении они будут равны нулю.

При программном изменении приоритета в версиях ARMv8-M без основного расширения и ARMv6-M соответствующее прерывание должно быть запрещено и не обрабатываться в момент изменения, иначе последствия будут непредсказуемыми (требование запрещённости не распространяется на прерывания SVCall и PendSV, которые не могут быть запрещены, но и не могут возникнуть неожиданно, без явного запроса со стороны программы). Версии ARMv8-M с основным расширением и ARMv7-M позволяют изменять приоритеты прерываний в любое время, однако в общем случае после этого необходимо выполнить следующие подряд команды DSB и ISB, обеспечивающие синхронизацию изменения приоритета с выполнением кода.

Приоритеты стандартных прерываний задаются в регистрах SHPR, приоритеты внешних прерываний — в регистрах контроллера прерываний IPR.

Группирование приоритетов

Приоритеты (не считая прерываний с фиксированными приоритетами) делятся на группы в соответствии со значением поля PRIGROUP регистра AIRCR. Полное 8-разрядное поле приоритета делится на две части: приоритет группы и подприоритет (приоритет внутри группы). Приоритет группы имеет длину от 7 до 0 битов для значений PRIGROUP от 0 до 7 соответственно; оставшуюся часть поля приоритета (1–8 разрядов) занимает подприоритет.

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

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

Каждое из трёх прерываний с фиксированным приоритетом считается образующим свою собственную отдельную группу (а HardFault в процессоре с расширением безопасности может образовывать две независимых группы – для уровней приоритетов –1 и –3).

Приоритет выполнения

Приоритет выполнения (приоритет текущего выполняемого процессором кода) зависит от следующих факторов:

  • выполняется ли в текущий момент поток или какой-либо обработчик прерывания;
  • если выполняется обработчик, то каков приоритет самого приоритетного из активных обработчиков;

Минимально возможным является так называемый базовый уровень приоритета выполнения (base level of execution priority) или просто базовый приоритет. Можно считать, что численно он равен приоритету самого низкоприоритетного из возможных прерываний плюс 1 (или, например, числу 256, которое гарантированно больше любого возможного уровня приоритета независимо от особенностей реализации процессора).

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

  • Если в данный момент активен сброс, приоритет выполнения равен –4. Реально на обработку прерываний этот случай не влияет, поскольку активный сигнал сброса полностью блокирует работу процессора и сбрасывает любые запросы прерываний, а после снятия сигнала начинается выполнение обработчика сброса. Формальное внесение этого пункта в документацию на архитектуру связано с тем, что сброс рассматривается авторами документации как самое приоритетное прерывание.
  • Если в данный момент активно прерывание HardFault безопасного режима и установлен бит AIRCR.BFHFNMINS, приоритет выполнения равен –3.
  • Если в данный момент активно немаскируемое прерывание (NMI), приоритет выполнения равен –2.
  • Если в данный момент активно прерывание HardFault небезопасного режима (сюда относится также случай, когда процессор не имеет расширения безопасности), а также если активно это прерывание безопасного режима и бит AIRCR.BFHFNMINS сброшен, приоритет выполнения равен –1.
  • Если в данный момент имеется хотя бы одно активное прерывание, за исключением сброса, NMI и HardFault, приоритет выполнения будет равен групповому приоритету самого приоритетного из активных прерываний (см. ниже).
  • Если же никаких активных прерываний нет, приоритет выполнения будет равен базовому приоритету, т. е. числу 256.

Групповой приоритет некоторого активного прерывания определяется следующим образом.

  • Если это прерывание обрабатывается в безопасном режиме или если процессор не имеет расширения безопасности, берётся полный приоритет этого прерывания (содержимое соответствующего регистра) и из него выделяются биты, задающие групповой приоритет; младшие биты, задающие приоритет внутри группы, обнуляются. Значения для этих вычислений на процессоре с расширением безопасности извлекаются из соответствующего регистра приоритета и регистра AIRCR безопасного режима.
  • Если это прерывание обрабатывается в небезопасном режиме, сначала производится определение его группового приоритета, как в предыдущем случае, только необходимые значения извлекаются из соответствующих регистров небезопасного режима. Однако затем анализируется состояние бита AIRCR_S.PRIS. Если этот бит сброшен, уже полученный приоритет будет приоритетом выполнения обработчика данного прерывания. Однако в случае, если этот бит установлен, полученный приоритет сдвигается вправо на 1 бит (численно делится на 2), после чего устанавливается его старший бит. Таким образом, установленный бит AIRCR_S.PRIS отображает весь диапазон значений приоритетов прерываний небезопасного режима (0–255) на младшую половину набора приоритетов (128–255).

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

В архитектурах ARMv8-M с основным расширением и ARMv-7 (а возможно, и ARMv8-M без основного расширения), если некоторый активный обработчик был вытеснен в связи с появлением более приоритетного запроса, он уже не может получить управление до тех пор, пока вытеснивший его обработчик не закончит своё выполнение, даже если в процессе выполнения приоритеты будут изменены, и вызванный позднее обработчик в итоге снизит свой приоритет до уровня, уступающего приоритету предыдущего обработчика. Однако снижение приоритета может обеспечить вытеснение текущего обработчика новым обработчиком, если приоритет нового обработчика после понижения приоритета текущего обработчика окажется в достаточной степени высоким.

В версии ARMv6-M (а возможно, и в ARMv8-M без основного расширения) изменение приоритета активного или разрешённого прерывания приведёт к непредсказуемым последствиям, а поэтому производиться не должно.

Регистры масок позволяют повысить текущий приоритет выполнения, ограничив тем самым возможность прерывания текущего кода независимо от приоритета самого этого кода.

  • Если расширение безопасности отсутствует, ненулевое значение в регистре BASEPRI (отсутствует в версиях ARMv8-M без основного расширения и ARMv6-M) задаёт приоритет, который будет использован в качестве приоритета выполнения, если он выше (численно меньше), чем самый высокий приоритет среди активных прерываний. В частности, если выполняется поток, BASEPRI повышает его приоритет до содержащегося в BASEPRI значения. Нулевое значение в BASEPRI означает, что этот регистр в определении текущего приоритета выполнения не участвует.
Если имеется расширение безопасности, у каждого из режимов безопасности есть свой регистр BASEPRI. Ненулевое значение безопасной версии регистра используется в неизменном виде, а небезопасной версии – в зависимости от состояния бита AIRCR.PRIS. Если этот бит сброшен, значение BASEPRI_NS также используется в неизменном виде. Если же этот бит установлен, исходное значение BASEPRI_NS сдвигается вправо на 1 бит (т. е. делится на 2), после чего производится установка старшего бита (исходный приоритет 0 превращается в 128, 64 – в 192, и т. д.).
  • Если расширение безопасности отсутствует, установленный бит PM регистра PRIMASK повышает текущий приоритет выполнения до нуля, т. е. запрещает все прерывания с программируемым приоритетом (разрешёнными остаются лишь HardFault, NMI и сброс). Если этот бит сброшен, он не влияет на определение текущего приоритета выполнения. Заметим, что установленный бит PM не препятствует прерыванию вывести процессор из ожидания, начатого командой WFI, но запрещает после пробуждения переходить к обработке данного прерывания: вместо этого процессор продолжит выполнять команды, следующие за WFI.
Если расширение безопасности имеется, то бит PM безопасной версии регистра PRIMASK ведёт себя так, как описано выше. Влияние небезопасной версии этого бита зависит от состояния бита AIRCR.PRIS: если последний сброшен, то установка PRIMASK_NS.PM повышает приоритет выполнения до нуля, если установлен – до 128.
  • Если расширение безопасности отсутствует, установленный бит FM регистра FAULTMASK (отсутствует в версиях ARMv8-M без основного расширения и ARMv6-M) повышает текущий приоритет до –1, запрещая все прерывания, кроме сброса и NMI. Он может быть установлен лишь при текущем уровне приоритета, численно равным или большим нуля; попытка установить этот бит в обработчике HardFault или NMI игнорируется. Если этот бит сброшен, он не участвует в определении текущего приоритета. Завершение обработки прерывания, за исключением возврата из NMI, автоматически сбрасывает бит FM.
Если расширение безопасности имеется, то процессор располагает двумя версиями регистра FAULTMASK. Установка безопасной версии бита FM повышает приоритет выполнения до –1, если бит AIRCR.BFHFNMINS сброшен, и до –3, если указанный бит установлен. Влияние установленной небезопасной версии бита FM зависит от состояния двух управляющих битов: AIRCR.BFHFNMINS и AIRCR.PRIS. Если первый из них установлен, приоритет выполнения повышается до –1; если он сброшен, то при сброшенном бите AIRCR.PRIS приоритет повышается до 0, а при установленном – до 128.

Итоговый приоритет выполнения равен численно наименьшему из всех значений, сформированных описанными выше способами.

Повышение приоритета запроса прерывания

Когда текущий приоритет выполнения ниже, чем HardFault (–1), процессор может поднять приоритет до этого уровня и войти в обработчик серьёзного отказа, если:

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

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

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

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

Адрес возврата из обработчика прерывания HardFault, вызванного в результате повышения приоритета запроса другого прерывания, определяется по правилам, относящимся к исходному запросу, а не к серьёзному отказу (например, для UsageFault он будет соответствовать команде, вызвавшей отказ, а для SVCall – команде, следующей за командой SVC).

Если процессор располагает сопроцессором с плавающей запятой, правила повышения приоритета во время сохранения контекста сопроцессор отличаются от вышеописанных (см. ниже).

Управление прерываниями

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

Первую группу образуют входящие в состав ядра процессора регистры IPSR, PRIMASK, FAULTMASK и BASEPRI (два последних в версиях ARMv8-M без основного расширения и ARMv6-M отсутствуют). Все они доступны привилегированному коду с помощью команд MSR и MRS; кроме того, состояние битов масок в регистрах PRIMASK и FAULTMASK может изменяться командой CPS. Первый из перечисленных регистров доступен только для чтения и содержит номер текущего обрабатываемого прерывания, три остальных позволяют изменить текущий приоритет выполнения и тем самым разрешить или запретить обработку прерываний с определёнными приоритетами.

Во вторую группу входят регистры пространства управления системой (SCS), из которых к управлению прерываниями относятся следующие:

  • VTOR — содержит базовый адрес таблицы векторов прерываний;
  • ICSR — позволяет получить информацию об активных и ожидающих прерываниях, а также программно запросить прерывания NMI, SysTick и PendSV и сбросить запросы двух последних прерываний;
  • AIRCR — управляет группированием приоритетов и поведением при сбросе, а также позволяет узнать порядок следования байтов, используемый для хранения информации в памяти;
  • SCR — управляет влиянием прерываний на режимы сна;
  • CCR — позволяет разрешить или запретить генерацию прерываний при делении на 0 и нарушении выравнивания, а также BusFault при текущем уровне привилегий –1 и выше;
  • SHPR1–SHPR3 — управляют приоритетами стандартных прерываний;
  • SHCSR — хранит информацию об активных и ожидающих прерываниях, а также разрешает или запрещает прерывания UsageFault, BusFault и MemManage;
  • STIR — позволяет программно вызвать появление запроса внешнего прерывания.

Третья группа представлена регистрами контроллера прерываний NVIC, а четвёртая — регистрами системного таймера SysTick. Они применяются для управления прерываниями от внешних устройств и от таймера соответственно.

Обработчики прерываний и расширение безопасности

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

  • Если использование некоторого сопроцессора запрещено только соответствующим битом регистра NSACR, но не регистром CPACR, попытка небезопасного кода обратиться к этому сопроцессору вызовет прерывание UsageFault безопасного режима.
  • Если доступ к некоторому сопроцессору запрещён регистром CPPWR, возникающее при попытке доступа прерывание UsageFault адресуется либо всегда безопасному режиму (если соответствующий бит SUS указанного регистра установлен), либо тому режиму безопасности, в котором была предпринята данная попытка доступа.
  • Прерывания, возникающие при попытке выборки команды, адресуются состоянию безопасности, связанному с адресом команды, а не текущему состоянию безопасности.
  • Если разрешена небезопасная обработка прерываний HardFault (бит AIRCR.BFHFNMINS установлен), то:
  • HardFault, являющийся результатом возникновения другого синхронного прерывания, которое не может быть обработано (см. выше), обрабатывается в том режиме безопасности, в котором возникло исходное прерывание;
  • HardFault, возникший в результате невозможности выборки вектора прерывания, адресуется тому состоянию безопасности, к которому относится прерывание, чей вектор не удалось выбрать.
  • Отказы, вызванные сохранением регистров вызываемой программы, всегда передаются безопасному обработчику.
  • Если небезопасная обработка HardFault запрещена (бит AIRCR.BFHFNMINS сброшен), возникновение небезопасного HardFault приводит к возникновению безопасного HardFault, при этом биты ожидающих прерываний небезопасного режима ведут себя так, как будто они сброшены, за исключением случаев их программного считывания или записи.
  • Если данная реализация имеет два таймера, прерывание от таймера адресуется соответствующему ему режиму безопасности, а не текущему режиму.

Прерывания NMI и BusFault передаются режиму безопасности, указанному битом AIRCR.BFHFNMINS (0 – всегда безопасному, 1 – текущему).

Прерывание SecureFault всегда адресуется безопасному обработчику.

Прерывание DebugMonitor адресуется безопасному режиму, если бит DEMCR установлен, и небезопасному режиму, если этот бит сброшен.

Каждое внешнее прерывание адресуется тому режиму безопасности, который задан соответствующим битом соответствующего регистра ITNS.

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

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

Начало и завершение обработки прерывания

Вход в обработчик прерывания

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

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

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

Концептуально при входе в обработчик выполняются следующие действия.

  • В текущем стеке сохраняется контекст прерванного кода. Объём сохраняемых данных зависит от особенностей процессора и от условий, в которых произошло прерывание.
  • Из таблицы векторов загружается адрес точки входа в обработчик.
  • Выполняется подготовка регистров к выполнению вызываемого обработчика:
  • в процессорах с расширением безопасности может быть выполнена очистка регистров процессора и FPU;
  • в регистр LR заносится код EXC_RETURN, обеспечивающий возврат из вызываемого обработчика;
  • в регистр PC заносится адрес точки входа в обработчик;
  • регистры управления и состояния модифицируются для обеспечения выполнения обработчика.
  • Устанавливается режим обработчика.
  • В процессорах с расширением безопасности устанавливается режим безопасности, нужный для выполнения вызываемого обработчика.
  • Сбрасывается признак ожидания для данного прерывания и устанавливается признак его активности.
  • Очищается монитор эксклюзивного доступа для данного процессора.
  • Если бит AIRCR.IESB установлен (это возможно лишь в процессорах с RAS), выполняется барьер синхронизации ошибок, что может привести к появлению запросов прерываний, которые фактически возникли раньше, но не могли быть обработаны до такой синхронизации.

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

Если в момент выполнения входа в обработчик некоторого прерывания появился более приоритетный запрос, то в зависимости от особенностей реализации либо происходит вход в обработчик нового прерывания (при этом предыдущее остаётся в состоянии ожидания), либо завершается вход в обработчик ранее существовавшего прерывания, после чего немедленно начинается вход в обработчик нового прерывания. В частности, если при выполнении барьера синхронизации ошибок появилось новое прерывание, может быть осуществлён вход в обработчик именно этого прерывания, а не того прерывания, которое обрабатывалось изначально (в такой ситуации последнее останется в состоянии ожидания).

Сохранение контекста прерванного кода

Сохраняемый при входе в обработчик прерывания контекст в общем случае состоит из четырёх частей:

  • основной контекст процессора (state context);
  • дополнительный контекст процессора (additional state context, только ARMv8-M с расширением безопасности);
  • основной контекст FPU (FP context или FP caller context, только процессоры с FPU или MVE);
  • дополнительный контекст FPU (additional FP context или FP callee context, только ARMv8-M с расширением безопасности и FPU/MVE).

Сохранение контекста производится в стеке прерванного кода. Место под него выделяется путём уменьшения текущего значения SP на число байтов, необходимое для сохранения контекста. Если значение SP на момент начала входа в обработчик прерывания не кратно 8, может быть выполнено его принудительное выравнивание, для чего SP дополнительно уменьшается на 4 (два младших бита SP всегда нулевые, поэтому кратность меньше 4 невозможна). Такое выравнивание выполняется в двух случаях:

  • если бит CCR.STKALIGN установлен (это всегда так в версиях ARMv6-M и ARMv8-M; версия ARMv7-M разрешает программно изменять значение этого разряда, но конкретная реализация может использовать фиксированное значение 1);
  • если необходимо сохранить контекст FPU или зарезервировать место под него.

Основной контекст процессора, занимающий восемь слов, сохраняется всегда. В эту область записывается содержимое регистров R0–R3, R12 и LR прерванного кода, адрес возврата к нему и используемое при возврате из обработчика значение PSR; последнее в документации на ARMv8-M носит обозначение RETPSR.

Адрес возврата, сохраняемый в области основного контекста, зависит от причины прерывания:

  • для любого синхронного прерывания, кроме SVCall (и HardFault, произошедшего вместо SVCall из-за необходимости повышения приоритета SVCall до HardFault), сохраняется адрес команды, вызвавшей данное прерывание;
  • для SVCall (и HardFault, произошедшего вместо SVCall) сохраняется адрес команды, следующей за SVC;
  • для любых асинхронных прерываний сохраняется адрес следующей подлежащей выполнению команды.

Младший бит в сохранённом адресе возврата всегда равен нулю.

RETPSR представляет собой копию значения XPSR (т. е. объединённого регистра состояния программы, включающего значения APSR, IPSR и EPSR) на момент вызова обработчика прерывания. Если данное прерывание вызвано ошибкой при выполнении команды, XPSR отражает состояние непосредственно перед попыткой выполнения этой команды; если команда сама по себе была исполнена нормально, XPSR соответствует моменту окончания выполнения этой команды.

Помимо обычных битов XPSR, в RETPSR используются ещё два бита:

  • бит 9 (SPREALIGN) устанавливается, если при выделении места в стеке под область сохранения потребовалось выравнивание значения SP;
  • бит 20 (SFPA) используется для сохранения значения бита CONTROL_S.SFPA, если был прерван безопасный код. Если был прерван небезопасный код, этот бит будет равен нулю.

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

В области дополнительного контекста процессора сохраняются регистры R4–R11 прерванного кода и так называемая сигнатура целостности (integrity signature). Последняя представляет собой значение FEFA125A, если при входе в обработчик должно было быть выделено место под контекст FPU (т. е. если был установлен бит CONTROL.FPCA), и FEFA125B в противном случае.

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

Основной контекст FPU (18 слов) включает регистры FPU S0–S15, FPSCR и, если имеется MVE, VPR. В документации на ARMv8-M эти регистры иногда называются «FP caller frame», поскольку подразумевается, что при вызове какой-либо подпрограммы за их сохранение отвечает вызывающая программа (а вызываемая программа может их свободно изменять).

Основной контекст, как правило, сохраняется или место под него резервируется (для последующего выполнения отложенного сохранения, если в этом возникнет необходимость), если установлен бит CONTROL.FPCA (т. е. если с момента последнего сохранения содержимое регистров FPU изменялось и разрешено автоматическое сохранение контекста FPU; последнее определяется установленным битом FPCCR.ASPEN). Однако процессор с расширением безопасности, обнаружив установленный бит CONTROL.FPCA, выполняет две дополнительные проверки.

  • Сначала выполняется проверка на разрушение предыдущего контекста FPU. Для этого анализируется состояние бита FPCCR.LSPACT для режима безопасности, заданного битом FPCCR_S.S (последний определяет, в каком режиме безопасности находился процессор на момент входа в ранее вызванный обработчик прерывания, при котором произошло выделение места под сохранение контекста FPU и было запрошено отложенное сохранение). Поскольку бит FPCCR.LSPACT устанавливается при входе в обработчик прерывания, когда запрашивается отложенное сохранение (при этом бит CONTROL.FPCA сбрасывается), и сбрасывается при фактическом сохранении контекста FPU (или при возврате из данного обработчика прерывания, если нужды в сохранении контекста так и не возникло), он, вообще говоря, не должен быть установлен одновременно с CONTROL.FPCA: такая комбинация означает, что подлежащие сохранению регистры FPU были изменены без выполнения сохранения, т. е. что контекст FPU был разрушен. В такой ситуации место под контекст FPU выделяется, однако его сохранение не выполняется; вместо этого формируется запрос прерывания SecureFault с установкой бита SFSR.LSERR.
  • При успешном прохождении предыдущей проверки проверяется доступность FPU для прерванного кода. Если процессор находится в небезопасном режиме и бит NSACR.CP10 сброшен (доступ небезопасного кода к FPU запрещён), место под контекст FPU не выделяется и формируется запрос безопасного прерывания UsageFault с установкой бита UFSR_S.NOCP. Тем не менее, в формируемых значениях сигнатуры целостности и EXC_RETURN будет отражено истинное состояние бита CONTROL.FPCA, указывающее на то, что место под контекст FPU было выделено.

Если описанных выше ошибок обнаружено не было, а бит FPCCR.LSPEN установлен, запрашивается отложенное сохранение, фактически выполняемое уже после входа в обработчик прерывания и лишь в случае, если в нём действительно возникнет нужда. Если же указанный бит сброшен, процессор пытается выполнить немедленное сохранение контекста FPU. Если это невозможно из-за того, что регистр CPACR запрещает доступ к FPU, сохранение не производится и формируется прерывание UsageFault текущего режима безопасности с установкой бита UFSR.NOCP.

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

Дополнительный контекст FPU (16 слов) сохраняется только процессором с расширением безопасности, если был прерван безопасный код и биты CONTROL.FPCA и FPCCR_S.TS установлены. Область под дополнительный контекст FPU не выделяется, если не удалось выделить место под основной контекст FPU из-за отсутствия доступа небезопасного кода к сопроцессору, как описано выше.

В области дополнительного контекста FPU сохраняются его регистры S16–S31. Их сохранение выполняется одновременно с сохранением основного контекста FPU, т. е. может производиться либо в момент вызова обработчика, либо позже. Эти регистры в документации иногда называются «FP callee frame», поскольку за сохранность этих регистров отвечает не вызывающая, а вызываемая программа. Аппаратное сохранение этих регистров в данном случае объясняется тем, что, во-первых, нельзя полагаться на правильное поведение небезопасного кода, т. е. что вызываемый небезопасный обработчик не изменит состояние этих регистров, а во-вторых, что небезопасному коду вообще нельзя предоставлять доступ к информации, относящейся к безопасному контексту, в том числе и к содержимому регистров, поэтому они должны быть очищены, а перед этим – сохранены в стеке безопасного режима (т. е. стеке прерванного кода). В то же время, если безопасный код не пользуется FPU, бит FPCCR_S.TS позволяет указать, что сохранять дополнительный контекст не требуется, поскольку он всегда относится к коду небезопасного режима.

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

Контекст не сохраняется или сохраняется лишь частично, если попытка сохранения вызывает отказ; в таком случае порождается соответствующий запрос на прерывание (SecureFault, MemFault, BusFault или HardFault; кроме того, возможна блокировка процессора).

В ARMv8-M, если выделенная область сохранения контекста вызывает нарушение пределов стека, то та часть контекста, которая не выходит за пределы стека, может быть сохранена, а может не сохраняться в зависимости от особенностей реализации; часть контекста, выходящая за пределы стека, никогда не сохраняется. Кроме того, в SP в этом случае заносится не адрес начала области сохранения, а адрес нижней границы стека, что лишает возможности определить состояние стека на момент возникновения прерывания. Заметим, что в процессоре с основным расширением контроль границ стека может быть отключён для прерываний NMI и HardFault установкой бита CCR.STKOFHFNMIGN соответствующего режима безопасности; в этом случае контекст сохраняется в полном объёме (если запись сама по себе не вызывает возникновения отказов MemFault и BusFault), а SP указывает на его начало.

Если при сохранении контекста возник какой-либо отказ при доступе к памяти (MemFault/BusFault) и одновременно была обнаружена ошибка, связанная с разрушением контекста FPU (SecureFault) или с невозможностью доступа к FPU со стороны небезопасного кода (UsageFault), в зависимости от реализации могут быть приняты либо оба возникших запроса прерывания, либо лишь запрос на SecureFault/UsageFault. Запрос на UsageFault по переполнению стека принимается всегда.

Архитектура задаёт порядок следования сохраняемых значений в памяти, но не порядок их фактической записи.

Структура области сохранения, сформированная при вызове данного обработчика, отражается значением EXC_RETURN, передаваемым обработчику через регистр LR (см. ниже).

На следующем рисунке представлена область сохранения контекста, включающая все четыре описанные выше части для архитектуры ARMv8-M, по этой причине поле, отведённое в ARMv8.1-M с MVE для сохранения регистра VPR, отмечено на нём как зарезервированное (смещение 8C). Заметим, что указатель стека прерванного кода, обновлённый в процессе входа в обработчик для выделения места под сохраняемый контекст, в данном случае указывает на начало области дополнительного контекста процессора. Если эта область не сохраняется, он будет указывать на область основного контекста процессора.

ARM M-profile exception context saving area

Инициализация регистров для вызываемого обработчика

Следующие регистры процессора при входе в обработчик прерывания имеют определённые значения:

  • SP: если вызываемый обработчик и прерванный код используют разные стеки, значение SP, используемого обработчиком, не изменяется, в противном случае в процессе вызова производится сохранение в стеке контекста прерванного кода, и SP будет указывать на начало этой области сохранения (кроме случая, если произошло нарушение границы стека; тогда SP равен адресу нижней границы);
  • LR: код EXC_RETURN;
  • PC: адрес точки входа в данный обработчик, выбранный из таблицы векторов прерываний; младший бит адреса всегда равен нулю;
  • EPSR.T: младший бит адреса точки входа, выбранного из таблицы векторов;
  • EPSR.IT: нуль;
  • IPSR: номер вектора прерывания;
  • PRIMASK, FAULTMASK, BASEPRI: не изменяются;
  • CONTROL.SPSEL: обнуляется;
  • CONTROL.FPCA: обнуляется;
  • CONTROL_S.SFPA: обнуляется.

При отсутствии расширения безопасности состояние регистров R0–R12 и APSR при входе в обработчик не определено. Технически изначально оно равно исходному содержимому этих регистров на момент начала входа в обработчик, однако, поскольку в общем случае порядок выполнения обработчиков может быть произвольным и они могут менять любые регистры, нельзя полагаться на то, что регистры будут содержать какие-то определённые значения.

При наличии расширения безопасности:

  • регистры R0–R3, R12 и APSR будут равны нулю, если вызывается небезопасный обработчик или процессор имеет версию не ниже ARMv8.1-M; в противном случае содержимое этих регистров не определено;
  • регистры R4–R11 обнуляются, если был прерван безопасный код и вызывается небезопасный обработчик, в противном случае их состояние не определено.

Если при сохранении контекста было выполнено в том числе фактическое сохранение контекста FPU (т. е. если бит CONTROL.FPCA на момент вызова обработчика был установлен, а бит FPCCR.LSPEN – сброшен), то:

  • если был прерван безопасный код и бит FPCCR_S.TS установлен, выполняется обнуление регистров S0–S31, FPSCR и, при наличии MVE, регистра VPR;
  • если был прерван небезопасный код либо если бит FPCCR_S.TS сброшен, регистры S16–S31 не изменяются, а состояние остальных перечисленных регистров не определено (в частности, оно может остаться неизменным).

Изменение перечисленных регистров FPU не производится, если выполнить сохранение не удалось из-за отсутствия доступа к FPU. Однако отказы в процессе записи контекста в память не препятствуют последующему изменению содержимого регистров FPU, что в такой ситуации означает утрату их содержимого на момент прерывания.

Если при сохранении контекста место под контекст FPU было успешно выделено, но само сохранение было отложено из-за установленного бита FPCCR.LSPEN, производится запоминание информации, необходимой для последующего отложенного сохранения контекста FPU:

  • в регистр FPCAR заносится адрес начала выделенной области сохранения контекста FPU;
  • если область сохранения контекста FPU нарушает нижнюю границу стека, устанавливается бит FPCCR.SPLIMVIOL; если нарушения нет, этот бит сбрасывается. Данная проверка осуществляется только в ARMv8-M. Она может быть заблокирована для NMI и HardFault установкой бита CCR.STKOFHFNMIGN. Заметим, что нарушение границы стека областью основного или дополнительного контекста процессора не проверяется;
  • биты FPCCR_S.S, FPCCR.USER и FPCCR.THREAD регистра FPCCR устанавливаются в соответствии с режимами выполнения прерванного кода;
  • бит FPCCR.HFRDY (он общий для обоих режимов безопасности) устанавливается, если на момент прерывания вход в обработчик HardFault возможен, т. е. если текущий приоритет выполнения численно больше, чем –1. Если вход невозможен, этот бит сбрасывается;
  • бит FPCCR.BFRDY (он общий для обоих режимов безопасности) устанавливается, если на момент прерывания вход в обработчик BusFault возможен, и сбрасывается в противном случае. Возможность входа определяется маской этого прерывания (бит SHCSR.BUSFAULTENA, общий для обоих режимов безопасности) и соотношением приоритета выполнения прерванного кода и приоритета прерывания BusFault для того режима безопасности, в котором оно должно обрабатываться в соответствии с состоянием бита AIRCR.BFHFNMINS;
  • бит FPCCR.MMRDY устанавливается, если на момент прерывания вход в обработчик MemManage возможен, и сбрасывается в противном случае. Возможность входа определяется состоянием бита маски SHCSR.MEMFAULTENA и приоритетом обработчика MemManage для режима безопасности прерванного кода;
  • изменяются биты FPCCR.UFRDY для обоих режимов безопасности. Каждый из них устанавливается, если в соответствующем режиме на момент прерывания вход в обработчик UsageFault возможен, и сбрасывается в противном случае. Возможность входа определяется битом маски SHCSR.USGFAULTENA и приоритетом обработчика UsageFault для соответствующего режима безопасности;
  • если процессор имеет расширение безопасности, то бит FPCCR_S.SFRDY устанавливается, если на момент прерывания вход в обработчик SecureFault возможен, и сбрасывается в противном случае. Возможность входа определяется битом маски SHCSR_S.SECUREFAULTENA и приоритетом обработчика SecureFault;
  • бит FPCCR.MONRDY (он общий для обоих режимов безопасности) устанавливается, если на момент прерывания вход в обработчик DebugMon возможен, и сбрасывается в противном случае. Вход в этот обработчик невозможен в следующих случаях:
  • отладочный монитор в данной реализации не поддерживается;
  • в момент прерывания разрешён отладочный останов процессора (с учётом режимов привилегированности и безопасности прерванного кода);
  • вызов монитора запрещён вообще (в процессоре без UDE сброшен бит DEMCR.MON_EN; в процессоре с UDE сброшен и этот бит, и бит DEMCR.UMON_EN) или только для безопасного состояния (бит DEMCR.SDME сброшен), а процессор находится в этом состоянии;
  • приоритет обработчика прерывания DebugMon ниже или равен текущему приоритету выполнения, т. е. обработчик DebugMon не может быть вызван (проверка проводится с учётом того, в безопасном или небезопасном режиме должен выполняться обработчик DebugMon, что управляется битом DEMCR.SDME).

Возникновение прерываний во время входа в обработчик прерывания

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

  • асинхронное внешнее прерывание от какого-либо устройства ввода-вывода;
  • отладочная точка наблюдения (при запрещённом отладочном останове);
  • прерывания MemManage и BusFault (или HardFault, если первые два не поддерживаются), связанные с попыткой сохранения контекста прерванного кода в его стеке;
  • в процессорах с расширением безопасности и FPU/MVE – прерывание SecureFault или UsageFault, возникающее при анализе состояния FPU перед немедленным выполнением сохранения его контекста или перед активацией отложенного сохранения;
  • в процессорах с расширением безопасности – прерывание SecureFault, если прерывание является небезопасным, однако выбранный вектор указывает на безопасную область памяти; при этом устанавливается бит INVEP. Этого, однако, не происходит, если вектор указывает на команду SG, находящуюся в безопасной области, допускающей вызов из небезопасного кода;
  • в процессорах с расширением безопасности – прерывание SecureFault, если прерывание является безопасным, а выбранный вектор указывает на небезопасную область памяти; при этом устанавливается бит INVTRAN;
  • в процессорах версии ARMv8-M – прерывание UsageFault (или HardFault, если UsageFault не поддерживается) по переполнению стека;
  • прерывание BusFault (или HardFault), связанное с попыткой выборки адреса точки входа из таблицы векторов прерываний.

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

  • немедленно после входа в обработчик возникнет новое прерывание, в результате чего вход в обработчик будет повторён уже для нового прерывания. Оба прерывания получат статус активных. Работа первоначального обработчика начнётся, когда завершится выполнение нового обработчика;
  • после сохранения контекста прерванного кода произойдёт вход не в первоначальный, а в новый обработчик. Прерывание, послужившее первопричиной начала процедуры входа, сохранит статус ожидающего; активным станет лишь новое, более приоритетное прерывание. После того, как его обработчик завершится, произойдёт вызов обработчика для ожидающего прерывания (как правило, с помощью сцепления прерываний).

Поведение процессора при остальных прерываниях зависит от текущего приоритета выполнения прерванного (вытесненного) кода, приоритета исходного прерывания (которое послужило причиной начала процедуры входа в обработчик и чей приоритет, следовательно, существенно выше, чем приоритет прерванного кода) и приоритета порождённого прерывания (возникшего вследствие выполнения процедуры входа в обработчик исходного прерывания). Кроме того, если одновременно возникли условия прерываний из-за отказа при сохранении контекста процессора и из-за недопустимого состояния FPU, что влечёт за собой SecureFault или UsageFault, то в зависимости от реализации либо сохраняются оба запроса на прерывание, либо только запрос, вызванный состоянием FPU (в последнем случае запрос прерывания MemManage или BusFault теряется).

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

Остальные случаи указывают на ошибку в процессе входа в обработчик. Ошибкам MemManage и BusFault, связанным с сохранением контекста, будет присвоен класс MSTKERR и STKERR соответственно, что найдёт своё отражение в регистрах состояния прерываний. Прерывание BusFault, связанное с попыткой считывания адреса обработчика из таблицы векторов прерываний, всегда рассматривается как серьёзный отказ и обрабатывается не как BusFault, а как HardFault.

Если приоритет прерывания, порождённого в результате ошибки, ниже или равен приоритету прерванного кода, т. е. если обработчик порождённого прерывания в соответствии с обычными правилами приоритетов немедленно вызван быть не может, произойдёт его повышение до уровня HardFault (приоритет –1). Исходное прерывание получит статус ожидающего и произойдёт вход в обработчик серьёзного отказа.

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

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

Завершение обработчика прерывания

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

Значение EXC_RETURN в архитектурах ARMv6-M и ARMv7-M идентифицируются по старшим четырём разрядам, в ARMv8-M – по старшим восьми разрядам (все они должны содержать единицы).

В процессе возврата из обработчика прерывания процессоры версий ARMv7-M и ARMv8-M (включая варианты без основного расширения) выполняют различные проверки корректности EXC_RETURN и некоторых других значений. В ARMv6-M никакие проверки корректности не выполняются и осуществляются лишь общие проверки на доступность данных в стеке. Обнаружение какой-либо ошибки вызывает формирование запроса соответствующего прерывания. Обработка таких запросов будет описана отдельно.

При нарушении любого из следующих требований к содержимому EXC_RETURN результаты будут непредсказуемы (в частности, процессор может не выполнять проверку указанных разрядов и, соответственно, выполнять возврат из прерывания, как если бы эти разряды имели требуемые значения):

  • биты 23:7 в ARMv8-M и 27:5 в ARMv7-M должны содержать единицы;
  • бит 1 в ARMv8-M должен быть равен нулю;
  • если процессор не поддерживает FPU/MVE, бит 4 (FType) должен быть единичным;
  • в ARMv8-M при отсутствии расширения безопасности биты 6 (S) и 0 (ES) должны быть равны нулю, а бит 5 (DCRS) – единице; в ARMv7-M все три перечисленных бита должны быть единичными. Таким образом, значение EXC_RETURN, программно сформированное для архитектуры ARMv6-M или ARMv7-M, не является полностью совместимым со значением EXC_RETURN для ARMv8-M без расширения безопасности (будет содержать единицу в бите 0).

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

  • В ARMv8-M с расширением безопасности проверяется, не пытается ли обработчик, фактически выполняющийся в небезопасном режиме, выполнить завершение с установленным битом EXC_RETURN.ES (ложно утверждая тем самым, что он является безопасным). При обнаружении этого нарушения:
  • бит EXC_RETURN.ES принудительно сбрасывается;
  • сбрасывается бит активности завершающегося прерывания (номер прерывания содержится в регистре IPSR) для режима безопасности, заданного битом EXC_RETURN.ES (в данном случае это небезопасный режим);
  • бит CONTROL.SPSEL, соответствующий режиму безопасности, определяемому битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL, чем восстанавливается выбор стека, имевший место до входа в завершающийся обработчик прерывания;
  • процесс возврата из прерывания прекращается и вместо этого формируется запрос прерывания SecureFault с установкой бита SFSR.INVER (в процессоре без основного расширения прерывание SecureFault отсутствует, поэтому запрашивается прерывание HardFault). При входе в обработчик, вызываемый сразу после этого, состояние бита EXC_RETURN.DCRS в регистре LR будет неопределённым (хотя сама ARM рекомендует, чтобы этот бит имел какое-либо определённое значение).
Заметим, что при завершении обработчика, реально выполняющегося в безопасном режиме, состояние бита EXC_RETURN.ES не проверяется, поэтому такой обработчик может завершить обработку прерывания, утверждая, что он является небезопасным.
  • В ARMv8-M с расширением безопасности, если сброшены оба бита EXC_RETURN.ES и EXC_RETURN.DSRS, выполняются те же действия по обработке ошибки, что описаны выше (за исключением сброса бита EXC_RETURN.ES, который и так сброшен).
  • Производится проверка, установлен ли бит активности для прерывания, чей обработчик завершается. Для выбора соответствующего регистра SHCSR или NVIC_IABR используются номер прерывания (находится в регистре IPSR) и режим безопасности, определяемый битом EXC_RETURN.ES. Если бит активности сброшен, выполняются следующие действия по обработке этой ошибки:
  • бит CONTROL.SPSEL, соответствующий режиму безопасности, определяемому битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL;
  • процесс возврата из прерывания прекращается и вместо этого формируется запрос прерывания UsageFault для режима безопасности, заданного битом EXC_RETURN.ES, с установкой бита UFSR.INVPC (в ARMv8-M без основного расширения прерывание UsageFault отсутствует, поэтому запрашивается прерывание HardFault).
  • В версии ARMv7-M, если бит CCR.NONBASETHRDENA сброшен и выполняется возврат в режим потока, проверяется, нет ли активных прерываний, за исключением завершаемого в данный момент. При нарушении этого требования выполняются описанные в предыдущем пункте действия по обработке ошибки.
Заметим, что в ARMv8-M бит CCR.NONBASETHRDENA формально отсутствует (фактически он зафиксирован в установленном состоянии), поэтому данная проверка не выполняется, что разрешает выполнять возврат в режим потока при наличии активных прерываний.
  • В версии ARMv7-M проверяются биты EXC_RETURN[3:0]: они могут быть равны лишь одной из разрешённых комбинаций (1, 9 или D). При нарушении этого выполняются описыванные выше действия по обработке ошибки.
  • Бит CONTROL.SPSEL, соответствующий режиму безопасности, заданному битом EXC_RETURN.ES, устанавливается равным биту EXC_RETURN.SPSEL.
  • Производится сброс бита активности завершающегося прерывания (номер прерывания содержится в IPSR) для заданного битом EXC_RETURN.ES режима безопасности.
  • В процессорах версии ARMv8-M с FPU/MVE и расширением безопасности, если одновременно установлены биты CONTROL.FPCA (завершающийся обработчик изменил регистры FPU) и FPCCR.CLRONRET (требуется очистка регистров FPU при завершении обработчика прерывания):
  • если установлен бит FPCCR_S.LSPACT (запрошено отложенное сохранение безопасного контекста FPU; в сочетании с установленным битом CONTROL.FPCA это означает, что контекст прерванного безопасного кода должен был быть сохранён, но фактически сохранён не был и поэтому оказался разрушен), формируется запрос прерывания SecureFault с установкой бита SFSR.LSERR; дальнейшие действия по возврату из обработчика не выполняются;
  • если версия архитектуры – ARMv8.1-M и завершающийся обработчик не имеет доступа к FPU (с учётом режима безопасности, указанного битом EXC_RETURN.ES), устанавливается бит USFR.NOCP и запрашивается прерывание UsageFault. Режим безопасности для запрашиваемого прерывания определяется причиной, блокирующей доступ к FPU: если доступ заблокирован регистром CPACR режима безопасности, заданного битом EXC_RETURN.ES, прерывание адресуется этому же режиму безопасности; если доступ блокирует регистр NSACR, прерывание адресуется безопасному режиму; если причиной блокировки является регистр CPPWR – прерывание адресуется безопасному режиму, если установлен бит SUS10 этого регистра, и режиму, заданному битом EXC_RETURN.ES, в противном случае. Проверки на блокировку осуществляются в перечисленном порядке. Дальнейшие действия по возврату из обработчика не выполняются;
  • регистры FPU S0–S15 (но не S16–S31), FPSCR и VPR обнуляются.
  • Если поддерживается сцепление прерываний и имеется какой-либо запрос прерывания, который можно обслужить немедленно, производится вызов обработчика этого прерывания без восстановления контекста прерванного ранее кода. Если же пригодных для немедленного обслуживания запросов нет или сцепление не поддерживается выполняются описываемые действия по восстановлению контекста прерванного кода.
Заметим, что сцепление прерываний в процессе возврата из обработчика ранее данного момента (т. е. выполнения перечисленных выше проверок, а также восстановления состояния бита CONTROL.SPSEL, сброса бита активности завершающегося прерывания и, возможно, обнуления регистров FPU) невозможно, однако оно может произойти не только в этой, но и в любой последующей точке возврата из обработчика. При этом, если процессор с расширением безопасности уже успел полностью или частично восстановить безопасный контекст, производится обнуление восстановленных регистров (если процессор не имеет расширения безопасности, эти регистры могут быть в произвольном состоянии).
  • Устанавливается режим безопасности, в который выполняется возврат (определяется значением бита EXC_RETURN.S).
  • Если установлены биты EXC_RETURN.Mode (возврат в режим потока) и SCR.SLEEPONEXIT и при этом активных прерываний больше нет, процессор переходит в состояние сна. Не совсем ясно, что произойдёт при выходе из сна при поступлении прерывания: либо восстановление контекста (которое на момент входа в сон выполнено ещё не было) с последующим прерыванием, либо сцепление прерываний. С практической точки зрения это влияет лишь на задержку вызова обработчика поступившего прерывания, но логично предположить, что, если сцепление возможно, произойдёт именно оно.
  • Производится восстановление регистров, сохранённых в стеке прерванного кода. Архитектура не определяет порядок восстановления регистров, а лишь общий набор выполняемых для этого действий. Любой отказ, вызванный попыткой считывания значения из стека, прекращает дальнейшую выборку данных из стека, а соответственно, восстановление контекста как таковое, и приводят к формированию соответствующего запроса прерывания (который, однако, в зависимости от реализации может быть потерян, если одновременно обнаруживается неверное состояние отложенного сохранения безопасного контекста FPU, о чём будет сказано ниже), но не отменяет других действий по возврату из обработчика. Такое поведение связано с тем, что невозможность корректного восстановления контекста прерванного кода препятствует возобновлению выполнения этого кода (в связи с чем и формируется запрос прерывания MemManage или BusFault), но не мешает завершить корректным образом обработчик прерывания – т. е. логически возникшее прерывание относится к прерванному коду, к которому выполняется возврат, а не к завершившемуся обработчику.
  • Определяются режимы доступа к памяти (соответствуют режимам прерванного кода, к которому осуществляется возврат) и выбирается стек, в котором находится подлежащий восстановлению контекст. Если при этом выбранный SP не кратен 8, результат будет непредсказуем.
  • В ARMv8-M с расширением безопасности, если был сохранён дополнительный контекст процессора (т. е. если бит EXC_RETURN.S установлен, а любой из битов EXC_RETURN.ES и EXC_RETURN.DCRS сброшен):
  • производится проверка сигнатуры целостности, которая была помещена в самое нижнее слово кадра стека, выделенного при входе в обработчик прерывания: она должна быть равна FEFA125A, если бит EXC_RETURN.FType сброшен, и FEFA125B, если он установлен. При обнаружении неверной сигнатуры формируется запрос прерывания SecureFault с установкой бита SFSR.INVIS и возврат из обработчика прекращается;
  • выполняется восстановление дополнительного контекста процессора (регистров R4–R11).
  • Восстанавливаются регистры основного контекста процессора (R0–R3, R12, LR, PC и PSR). При восстановлении двух последних регистров выполняются дополнительные действия:
  • считанные из стека разряды PC[31:1] помещаются в соответствующий регистр процессора; бит 0 восстановленного значения игнорируется;
  • производится проверка соответствия значения IPSR, восстановленного из стека (биты PSR[8:0]), режиму выполнения кода, к которому производится возврат (задаётся битом EXC_RETURN.Mode). Если происходит возврат в режим потока, а IPSR отличен от нуля, либо возврат в режим обработчика, а IPSR равен нулю, формируется запрос прерывания UsageFault с установкой бита UFSR.INVPC и процесс возврата из прерывания прекращается;
  • если восстановленное значение IPSR проходит описанную выше проверку, но при этом не соответствует ни одному имеющемуся в данной реализации прерыванию, реальное значение, помещённое в IPSR, является непредсказуемым;
  • бит 9 восстановленного значения PSR будет использоваться позже при увеличении указателя стека, чтобы при необходимости отменить выполненное при входе в обработчик прерывания принудительное выравнивание стека на границу 8 байтов;
  • в процессоре с расширением безопасности значение бита 20 восстановленного значения PSR будет позже использоваться для установки бита CONTROL_S.SFPA;
  • остальные значащие биты PSR, восстановленные из стека, заносятся в соответствующие разряды регистра PSR процессора.
  • Если процессор имеет FPU/MVE:
  • если бит EXC_RETURN.FType сброшен (прерванный код изменял регистры FPU, поэтому они должны быть восстановлены):
  • если в ARMv8-M с расширением безопасности выполняется возврат в небезопасный режим (бит EXC_RETURN.S сброшен) и бит FPCCR_S.LSPACT установлен (было запланировано, но не было выполнено сохранение безопасного контекста FPU), формируется запрос прерывания SecureFault с установкой бита SFSR.LSERR. Если при этом ранее возник отказ при доступе к стеку для выборки сохранённых значений основного или дополнительного контекста процессора, то в зависимости от реализации запрос прерывания MemManage/BusFault может сохраняться вместе с SecureFault или может быть сохранён только SecureFault;
  • если предыдущая проверка была успешна, проверяется, не активен ли запрос на отложенное сохранение для режима безопасности, в который выполняется возврат (т. е. проверяется состояние бита FPCCR.LSPACT для уже установленного по значению бита EXC_RETURN.S режима безопасности). Если этот бит установлен, отложенное сохранение не выполнялось, т. е. регистры FPU по-прежнему содержат контекст прерванного кода; в такой ситуации бит FPCCR.LSPACT сбрасывается и никаких других действий с контекстом FPU не выполняется;
  • если же бит FPCCR.LSPACT был сброшен (что вместе со сброшенным EXC_RETURN.FType означает, что контекст FPU был фактически сохранён в стеке), проверяется доступность FPU (CP10) для кода, к которому выполняется возврат (с учётом его режимов безопасности и привилегированности – последний определяется по состоянию бита CONTROL.nPRIV для текущего режима безопасности, ранее установленного по значению бита EXC_RETURN.S). Если FPU недоступен, запрашивается прерывание UsageFault с установкой бита UFSR.NOCP и восстановление контекста FPU не выполняется, хотя сам процесс завершения обработчика прерывания продолжается. Если же FPU доступе, производится восстановление его регистров S0–S15, FPSCR и VPR (последний имеется только в процессорах с MVE). Кроме того, если производится возврат в безопасный режим (EXC_RETURN.S установлен) и бит FPCCR_S.TS установлен (контекст FPU считается безопасным), производится восстановление дополнительного контекста FPU (регистры S16–S31). Если в процессе восстановления контекста FPU возник отказ, то при наличии расширения безопасности, восстановленные регистры обнуляются, а при его отсутствии значения регистров являются неопределёнными.
  • Бит CONTROL.FPCA устанавливается равным инверсному значению бита EXC_RETURN.FType.
  • Если на предыдущих шагах не возникло прерываний по ошибкам, значение SP увеличивается на размер сохранённого контекста (с учётом возможного принудительного выравнивания SP, что определяется битом 9 восстановленного значения PSR).
  • В процессоре с расширением безопасности бит CONTROL_S.SFPA устанавливается равным 20-му биту восстановленного из стека значения PSR.
  • Если никаких отказов в процессе восстановления регистров обнаружено не было, выполняются завершающие действия по выходу из прерывания:

Сцепление прерываний

Сцепление прерываний (tail-chaining) — это механизм, позволяющий сократить время переключения с одного обработчика прерывания на другой в момент завершения первого обработчика. Это достигается тем, что, обнаружив наличие ожидающего запроса прерывания с групповым приоритетом более высоким, чем приоритет выполнения кода, к которому осуществляется возврат (с учётом состояния регистров масок), процессор не выполняет возврат из прерывания с немедленным повторным входом в новый обработчик, а сразу передаёт управление новому обработчику, при этом в LR на момент входа в обработчик нового прерывания будет находиться значение EXC_RETURN, заданное при завершении предыдущего обработчика.

Формат EXC_RETURN

Формат значения EXC_RETURN показан на рисунке:

ARM M-profile EXC RETURN
  • Биты 31:24 все равны 1, что определяет, что данный код – EXC_RETURN. В версиях ARMv6-M и ARMv7-M идентификатором служили четыре старших бита, а разряды 27:24, хотя и устанавливались процессором в 1, при выполнении возврата из обработчика прерывания игнорировались.
  • Биты 23:7 устанавливаются равными 1, но сами по себе они являются зарезервированными и при возврате из прерывания могут игнорироваться.
  • Бит S (разряд 6) определяет, в каком режиме безопасности находился процессор в момент начала вызова обработчика (т. е. стек какого режима был использован для сохранения контекста):
  • 0 – небезопасный режим;
  • 1 – безопасный режим.
Если расширение безопасности отсутствует, значение этого разряда непредсказуемо.
  • Бит DCRS (разряд 5) указывает, осуществлялось ли сохранение дополнительного контекста процессора. Для правильной интерпретации этого бита необходимо также учитывать значения битов S и ES (т. е. в каком режиме безопасности работал прерванный код и в каком режиме безопасности работает вызываемый обработчик).
  • 0 – Это состояние возможно лишь при прерывании безопасного кода (S = 1) и вызове безопасного обработчика (ES = 1), если при этом, однако, было выполнено сохранение дополнительного контекста. Такое сохранение могло произойти, например, из-за того, что сначала был вызван небезопасный обработчик (что требует сохранения дополнительного контекста), а затем при завершении последнего произошло сцепление с безопасным обработчиком.
  • 1 – Всегда устанавливается, если был прерван небезопасный код (S = 0); в этом случае дополнительный контекст не сохраняется. Если был прерван безопасный код (S = 1), то для безопасного обработчика (ES = 1) установленный бит DCRS означает, что дополнительный контекст не сохранялся, а для небезопасного (ES = 0) – что сохранялся (последнее имеет место всегда при прерывании безопасного кода для вызова небезопасного обработчика).
  • Бит FType (разряд 4), вообще говоря, указывает, выделялось ли в стеке место под сохранение контекста FPU:
  • 0 – место выделялось;
  • 1 – место не выделялось.
Технически этот бит равен инверсному значению бита CONTROL.FPCA на момент начала вызова обработчика. По этой причине, если выделение места под контекст FPU не производилось из-за того, что был прерван код небезопасного режима и его доступ к FPU заблокирован битом NSACR.CP10, бит FType всё равно будет сброшен, как будто место было выделено. Такая ситуация, однако, является ненормальной и вызывает формирование запроса безопасного прерывания UsageFault по отсутствию сопроцессора.
  • Бит Mode (разряд 3) определяет режим выполнения прерванного кода, чей контекст был сохранён:
  • 0 – режим обработчика;
  • 1 – режим потока.
  • Бит SPSEL (разряд 2) определяет, какой стек использовался для сохранения контекста:
  • 0 – основной стек;
  • 1 – стек процесса.
  • Бит 1 зарезервирован и равен 0.
  • Бит ES (разряд 0) определяет режим безопасности вызванного обработчика прерывания:
  • 0 – небезопасное прерывание;
  • 1 – безопасное прерывание.
Если расширение безопасности отсутствует, состояние этого бита непредсказуемо.

В ARMv6-M и ARMv7-M биты 6, 5 и 0 должны быть равны единице, что делает их формат EXC_RETURN не совместимым в полной мере с форматом для ARMv8-M.

Дополнительная информация

Блокировка при невозможности входа в обработчик прерывания

Если при выполнении кода с приоритетом –1 или выше (обработчика серьёзного отказа или NMI, а также любого кода при установленном бите FM регистра FAULTMASK) возникнет прерывание, оно не сможет быть обработано, поскольку приоритет его обработчика не может быть выше 0, а с помощью повышения его приоритета нельзя получить приоритет выше –1. В этом случае процессор выполняет следующие действия:

  • устанавливает бит S_LOCKUP в регистре состояния и управления отладочными остановами DHCSR;
  • устанавливает бит регистра состояния прерываний, отвечающий за причину, приведшую к возникновению прерывания, которое невозможно обработать;
  • зацикливается на команде, послужившей причиной возникновения прерывания, а если причиной была не команда, а выполнение какого-либо другого действия, например, попытка сохранения или восстановления контекста, — на «команде», выбираемой по адресу FFFFFFFE. Адрес, на котором произошло зацикливание, называется адресом блокировки (lockup address).
В архитектуре ARMv6-M зацикливание всегда происходит на адресе FFFFFFFE.

Блокировка с зацикливанием по адресу FFFFFFFE происходит по следующим причинам:

  • неудачная попытка считывания адреса или начального значения SP при сбросе;
  • неудачная попытка считывания вектора NMI или HardFault;
  • BusFault при попытке сохранения или восстановления контекста;
  • MemManage при попытке сохранения или восстановления контекста;
  • UsageFault при возврате из прерывания в связи с недопустимым значением PC.

Блокировка с зацикливанием по адресу команды, вызвавшей ошибку, происходит по следующим причинам:

  • BusFault при попытке выборки команды. Если через некоторое время причина возникновении ошибки будет устранена (например, первопричиной ошибки шины послужило «сонное» состояние памяти), эта ошибка будет автоматически устранена, и процессор, выбрав команду, продолжит нормальную работу;
  • точный BusFault при обращении к данным. Эта ошибка будет проигнорирована, если установлен бит BFHFNMIGN в регистре CCR (процессор ограничится лишь установкой индикатора ошибки в соответствующем регистре состояния). Как и в предыдущем случае, блокировка может быть снята, если ошибка через некоторое время исчезнет;
  • MemManage при попытке выборки команды. Эта ошибка всегда приведёт к зацикливанию по адресу команды, если производится попытка её выборки из адресного пространства, недоступного для исполнения, однако может быть проигнорирована в остальных случаях, если установлен бит HFNMIENA регистра MPU_CTRL;
  • MemManage при попытке доступа к данным. Эта ошибка игнорируется, если установлен бит HFNMIENA регистра MPU_CTRL;
  • попытка выполнения команды SVC. Сама команда в этом случае рассматривается как неопределённая и никаких действий, кроме зацикливания, не вызывает;
  • UsageFault, связанный с какой-либо конкретной командой;
  • срабатывание точки останова.

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

  • если зацикливание имеет место при приоритете –1, NMI вызовет обычный вход в обработчик NMI, имеющий более высокий приоритет (–2). Адресом возврата из этого обработчика будет адрес блокировки;
  • с помощью сброса, в том числе от сторожевого таймера;
  • путём останова внешним отладчиком.

Прерываемые команды

Некоторые команды могут быть реализованы как прерываемые. В случае поступления прерывания во время выполнения такой команды она будет прервана, причём информация о том, какая часть уже проделана, будет сохранена в поле ICI регистра EPSR. К числу потенциально прерываемых относятся команды LDM, STM, POP, PUSH, VLDM, VSTM, VPOP и VPUSH (последние четыре относятся к набору команд FPU). После возврата из прерывания выполнение прерываемой команды возобновится с точки, где оно было прервано. В других реализациях при возникновении прерывания выполнение перечисленных команд может прекращаться, а после возврата из прерывания — начинаться с самого начала.

Если прерываемая команда встречается внутри блока IT, в регистре EPSR будет сохранено состояние выполнения этого блока (поле IT), а не состояние прерываемой команды. В этом случае выполнение последней после возврата из обработчика прерывания начнётся с самого начала.

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

Отложенное сохранение контекста FPU

Если в момент входа в обработчик прерывания бит CONTROL.FPCA установлен (его установленное состояние означает, что после последнего сохранения контекста FPU содержимое его регистров изменилось, т. е. выполнялись какие-либо команды FPU или векторного расширения), процессор выделяет в стеке прерванного кода место под сохранение основного и, возможно, дополнительного контекста FPU. Если при этом сброшен бит FPCCR.LSPEN (т. е. если отложенное сохранение контекста FPU запрещено), при сохранении контекста процессора будет выполнено и сохранение контекста FPU. Если был прерван безопасный код и установлен бит FPCCR_S.TS, сохраняется также расширенный контекст FPU.

Если бит FPCCR.LSPEN установлен (отложенное сохранение разрешено), то в момент входа в обработчик в стеке выделяется место под контекст FPU (и под расширенный контекст FPU, если прерван безопасный код и установлен бит FPCCR_S.TS), однако само сохранение не производится. Вместо этого в регистре FPCAR сохраняется адрес начала области контекста FPU (адрес слова, в которое должен сохраняться регистр S0), а бит FPCCR.LSPACT устанавливается, отмечая активное состояние отложенного сохранения. Кроме того, в регистре FPCCR запоминается информация о режимах и разрешённых отказах на момент возникновения прерывания; эти сведения будут использованы в случае выполнения отложенного сохранения контекста FPU.

Если позднее при установленном бите FPCCR.LSPACT будет предпринята попытка выполнить какую-либо команду обработки вещественных чисел (FPU или MVE, включая коды команд, относящиеся к CP10, но не выделенные конкретным командам), перед её фактическим выполнение производится отложенное сохранение контекста FPU.

  • Основываясь на информации о режимах, активных на момент входа в обработчик прерывания, при котором было выделено место для отложенного сохранения контекста (она была занесена в соответствующие биты регистра FPCCR), проверяется возможность использования FPU для прерванного кода (для этого анализируются соответствующие разряды регистров CPACR и NSACR).
Если окажется, что использование FPU было запрещено, его контекст не сохраняется и возникнет прерывание UsageFault по недоступности сопроцессора. Однако, если программа пытается выполнить одну из команд FPU VLLDM, VLSTM или VSCCLRM, возникает прерывание UsageFault по неопределённой команде вместо прерывания по недоступности сопроцессора.
  • Если доступ к FPU возможен, выполняется сохранение его контекста по адресу, находящемуся в регистре FPCAR. Основной контекст сохраняется всегда, дополнительный – только если установлены биты FPCCR.TS и FPCCR_S.S.
Если в процессе сохранения контекста FPU были обнаружены какие-либо отказы, возникает соответствующее прерывание.
  • Если сохранение контекста выполнено успешно, бит FPCCR.LSPACT сбрасывается.
  • Если команда, инициировавшая выполнение сохранения контекста, выполняется в небезопасном состоянии, регистры FPU устанавливаются в неизвестное состояние, если в безопасном – очищаются.
  • Производится фактическое выполнение команды, вызвавшей сохранение контекста.