Сохраняются ли регистры стекирования ARM Cortex-M0 в $ psp или $ msp во время аппаратного сбоя?

У меня проблема, связанная с серьезной неисправностью моего Cortex-M0, поэтому я пытаюсь отладить ее. Я пытаюсь распечатать содержимое регистров ядра ARM, которые были помещены в стек при возникновении аппаратной ошибки.

Вот мой базовый код сборки:

__attribute__((naked)) void HardFaultVector(void) {
    asm volatile(
        // check LR to see whether the process stack or the main stack was being used at time of exception.
        "mov r2, lr\n"
        "mov r3, #0x4\n"
        "tst r2, r3\n"
        "beq _MSP\n"

        //process stack was being used.
        "_PSP:\n"
        "mrs r0, psp\n"
        "b _END\n"

        //main stack was being used.
        "_MSP:\n"
        "mrs r0, msp\n"
        "b _END\n"

        "_END:\n"
        "b fault_handler\n"
    );  
}  

Функция fault_handler распечатает содержимое кадра стека, который был передан либо в стек процесса, либо в основной стек. Но вот мой вопрос:

Когда я печатаю содержимое кадра стека, который предположительно имеет сохраненные регистры, я вижу вот что:

Stack frame at 0x20000120:
 pc = 0xfffffffd; saved pc 0x55555554
 called by frame at 0x20000120, caller of frame at 0x20000100
 Arglist at unknown address.
 Locals at unknown address, Previous frame's sp is 0x20000120
 Saved registers:
  r0 at 0x20000100, r1 at 0x20000104, r2 at 0x20000108, r3 at 0x2000010c, r12 at 0x20000110, lr at 0x20000114, pc at 0x20000118, xPSR at 0x2000011c

Вы можете видеть сохраненные регистры, это регистры, которые передаются ядром ARM при возникновении аппаратной ошибки. Вы также можете увидеть строку pc = 0xfffffffd;, которая указывает, что это значение EXC_RETURN LR. Значение 0xfffffffd указывает мне, что стек процессов использовался во время аппаратной ошибки.

Если я напечатаю значение $psp, я получу следующее:

gdb $ p/x $psp
$91 = 0x20000328

Если я напечатаю значение $msp, я получу следующее:

gdb $ p/x $msp
$92 = 0x20000100

Вы можете ясно видеть, что $msp указывает на вершину стека, где предположительно находятся сохраненные регистры. Не означает ли это, что в главном стеке есть сохраненные регистры, которые ядро ​​ARM поместило в стек?

Если я распечатаю содержимое памяти, начиная с адреса $msp, я получу следующее:

gdb $ x/8xw 0x20000100
0x20000100 <__process_stack_base__>:    0x55555555  0x55555555  0x55555555  0x55555555
0x20000110 <__process_stack_base__+16>: 0x55555555  0x55555555  0x55555555  0x55555555

Пусто...

Теперь, если я распечатаю содержимое памяти, начиная с адреса $psp, я получу следующее:

gdb $ x/8xw 0x20000328
0x20000328 <__process_stack_base__+552>:    0x20000860  0x00000054  0x00000054  0x20000408
0x20000338 <__process_stack_base__+568>:    0x20000828  0x08001615  0x1ad10800  0x20000000

Это выглядит более точным. Но я думал, что сохраненные регистры должны указывать, где они находятся во флэш-памяти? Так какой в ​​этом смысл?


person cDreamer    schedule 29.08.2018    source источник
comment
При вытеснении потока команд оборудование сохраняет состояние контекста в стек, на который указывает один из регистров SP, см. Регистры SP на стр. B1-186. Используемый стек зависит от режима процессора во время исключения.   -  person old_timer    schedule 30.08.2018
comment
Указатель стека, который используется при входе и выходе исключения, описан в последовательностях псевдокода входа и выхода исключения, см. «Поведение при входе исключения» на стр. B1-196 и «Поведение при возврате исключения» на стр. B1-198. SP_main выбирается и инициализируется при сбросе, см. Поведение при сбросе на стр. B1-195.   -  person old_timer    schedule 30.08.2018
comment
почему вы хотите возиться с msp?   -  person old_timer    schedule 30.08.2018
comment
вам следует начать с реального языка ассемблера, чтобы никакие компиляторы не вмешивались в то, что вы пытаетесь сделать.   -  person old_timer    schedule 30.08.2018
comment
Привет, @old_timer! Еще раз спасибо! Я предполагаю, что вы имели в виду Справочное руководство ARMvxM, когда упоминали номера страниц. Я просмотрел там псевдокод и убедился, что стек процессов используется, когда возникает исключение, и что стек процессов - это то место, куда помещаются основные регистры. Я не обязательно пытаюсь связываться с msp, мне просто любопытно, почему проталкиваемые регистры имеют адреса, связанные с msp, а не связанные с psp. Вы знаете, почему это могло быть?   -  person cDreamer    schedule 30.08.2018
comment
Я читаю документацию по руке, относящуюся к cortex-m0. в первую очередь почему вы пытаетесь использовать msp? Во-вторых, как описано в документации, какой стек используется, зависит от того, что происходило, когда произошло исключение.   -  person old_timer    schedule 30.08.2018


Ответы (1)


Все комментарии пользователя old_timer к вашему вопросу верны. Регистры будут помещены в активный стек при записи исключения, независимо от того, является ли это PSP или MSP в данный момент. По умолчанию весь код использует основной стек (MSP), но если вы используете что-либо иное, кроме полностью «голого металла», вполне вероятно, что какое-либо ядро, которое вы используете, переключило режим потока на использование стека процессов (PSP).

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

Также помните, что одна из распространенных причин для входа в обработчик HardFault - это то, что другой обработчик исключений вызвал исключение. Это легко может произойти в случае повреждения памяти. В этих случаях (при условии, что режим потока использует PSP) ЦП сначала войдет в режим обработчика в ответ на исходное исключение, отправив r0-r3,r12,lr,pc,psr в стек процесса. Он начнет выполнение исходного обработчика исключений, затем снова откажется, помещая r0-r3,r12,lr,pc,psr в основной стек при входе в обработчик HardFault. Часто приходится кое-что делать.

old_timer также упоминает использование реального языка ассемблера, и я с этим тоже согласен. Даже несмотря на то, что атрибут ((naked)) должен удалять пролог и эпилог (между ними большинство возможных «компиляторов»), ваш код был бы намного более читабельным, если бы он был написан на чистом ассемблере. Встроенный язык ассемблера имеет свое применение, например, если вы хотите сделать что-то очень низкоуровневое, чего нельзя сделать с помощью C, но вы хотите избежать накладных расходов при вызове-возврате. Но когда вся ваша функция написана на ассемблере, нет смысла ее использовать.

person cooperised    schedule 30.08.2018