Инициализация стека исключений ARM ядра Linux

Я использую ядро ​​​​Linux 3.0.35 на Freescale i.MX6 (ARM Cortex-A9). После запуска ядра OOPS я попытался понять инициализацию стека исключений. Вот что я обнаружил до сих пор.

В cpu_init() в arch/arm/kernel/setup.c я вижу инициализацию стека исключений:

struct stack {
    u32 irq[3];
    u32 abt[3];
    u32 und[3];
} ____cacheline_aligned;

static struct stack stacks[NR_CPUS];

void cpu_init(void)
{
    struct stack *stk = &stacks[cpu];

    ...<snip>

    /*
     * setup stacks for re-entrant exception handlers
     */
    __asm__ (
     "msr   cpsr_c, %1\n\t"
    "add    r14, %0, %2\n\t"
    "mov    sp, r14\n\t"
    "msr    cpsr_c, %3\n\t"
    "add    r14, %0, %4\n\t"
    "mov    sp, r14\n\t"
    "msr    cpsr_c, %5\n\t"
    "add    r14, %0, %6\n\t"
    "mov    sp, r14\n\t"
    "msr    cpsr_c, %7"
        :
        : "r" (stk),
          PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
          "I" (offsetof(struct stack, irq[0])),
          PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
          "I" (offsetof(struct stack, abt[0])),
          PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
          "I" (offsetof(struct stack, und[0])),
          PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
        : "r14");

Я вижу, что в каждом стеке есть место только для трех слов. Именно так его использует макрос vector_stub в arch/arm/kernel/entry-armv.S. Он сохраняет R0, LR (родительский компьютер) и SPSR (родительский CPSR) в эти три слова. Затем он переходит к __irq_svc. Это начинается с макроса svc_entry, который создает кадр стека.

    .macro  svc_entry, stack_hole=0
 UNWIND(.fnstart        )
 UNWIND(.save {r0 - pc}     )
    sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)

Так же я вижу дизассемблированный код из KGDB:

Dump of assembler code for function __irq_svc:
   0xc01402c0 <+0>:  44 d0 4d e2    sub sp, sp, #68 ; 0x44
   0xc01402c4 <+4>:  04 00 1d e3    tst sp, #4
   0xc01402c8 <+8>:  04 d0 4d 02    subeq   sp, sp, #4
   0xc01402cc <+12>:     fe 1f 8d e8    stm sp, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}
   0xc01402d0 <+16>:     0e 00 90 e8    ldm r0, {r1, r2, r3}
   0xc01402d4 <+20>:     30 50 8d e2    add r5, sp, #48 ; 0x30
   0xc01402d8 <+24>:     00 40 e0 e3    mvn r4, #0
   0xc01402dc <+28>:     44 00 8d e2    add r0, sp, #68 ; 0x44
   0xc01402e0 <+32>:     04 00 80 02    addeq   r0, r0, #4
   0xc01402e4 <+36>:     04 10 2d e5    push    {r1}        ; (str r1, [sp, #-4]!)
   0xc01402e8 <+40>:     0e 10 a0 e1    mov r1, lr
   0xc01402ec <+44>:     1f 00 85 e8    stm r5, {r0, r1, r2, r3, r4}
   0xc01402f0 <+48>:     ad 96 a0 e1    lsr r9, sp, #13
   0xc01402f4 <+52>:     89 96 a0 e1    lsl r9, r9, #13
   0xc01402f8 <+56>:     04 80 99 e5    ldr r8, [r9, #4]
   0xc01402fc <+60>:     01 70 88 e2    add r7, r8, #1
   0xc0140300 <+64>:     04 70 89 e5    str r7, [r9, #4]
   0xc0140304 <+68>:     54 50 9f e5    ldr r5, [pc, #84]   ; 0xc0140360
   0xc0140308 <+72>:     00 50 95 e5    ldr r5, [r5]
   0xc014030c <+76>:     0c 60 95 e5    ldr r6, [r5, #12]
   0xc0140310 <+80>:     4c e0 9f e5    ldr lr, [pc, #76]   ; 0xc0140364
   0xc0140314 <+84>:     07 0b c6 e3    bic r0, r6, #7168   ; 0x1c00
   0xc0140318 <+88>:     1d 00 50 e3    cmp r0, #29
   0xc014031c <+92>:     00 00 50 31    cmpcc   r0, r0
   0xc0140320 <+96>:     0e 00 50 11    cmpne   r0, lr
   0xc0140324 <+100>:    00 00 50 21    cmpcs   r0, r0
   0xc0140328 <+104>:    0d 10 a0 11    movne   r1, sp
   0xc014032c <+108>:    28 e0 4f 12    subne   lr, pc, #40 ; 0x28
   0xc0140330 <+112>:    32 eb ff 1a    bne 0xc013b000 <asm_do_IRQ>
   0xc0140334 <+116>:    04 80 89 e5    str r8, [r9, #4]
   0xc0140338 <+120>:    00 00 99 e5    ldr r0, [r9]
   0xc014033c <+124>:    00 00 38 e3    teq r8, #0
   0xc0140340 <+128>:    00 00 a0 13    movne   r0, #0
   0xc0140344 <+132>:    02 00 10 e3    tst r0, #2
   0xc0140348 <+136>:    06 00 00 1b    blne    0xc0140368 <svc_preempt>
   0xc014034c <+140>:    40 40 9d e5    ldr r4, [sp, #64]   ; 0x40
   0xc0140350 <+144>:    04 f0 6f e1    msr SPSR_fsxc, r4
   0xc0140354 <+148>:    1f f0 7f f5    clrex
   0xc0140358 <+152>:    ff ff dd e8    ldm sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc}^
End of assembler dump.

Во время исключения SP является банком R13. Если я правильно понимаю, в этом стеке нет места для этого фрейма. Это значит, что я что-то пропустил. Есть ли какое-то другое место, где инициализируются стеки исключений?


person Dark Matter    schedule 08.04.2014    source источник


Ответы (1)


tl;dr — мы переключаем режимы на супервизора и используем этот стек.

Вы упускаете ключевой момент, когда управление передается ЦП через векторную таблицу и режим переключается. См.: entry-armv.S и __vectors_start. vector stubs — это код, в котором управление изначально отправляется после branch в основной таблице векторов. Макрос vector_stub сохраняет три элемента; исправленные lr, r0 и spsr исключенного режима (как вы заметили).

Вы упускаете момент, после которого все исключения переключаются на SVC_MODE и, таким образом, используют стек задач current, который также имеет структуру thread_info. переключение режима — сложная концепция для ассемблера системного уровня ARM. Регистры, которые были установлены ранее, теперь совершенно другие. Обратите внимание на инструкции типа msr и cps. После них все может полностью измениться; Я был сбит с толку этим десятки раз.

spsr используется в качестве индекса в таблице vector_stub, которая обычно переходит либо к __irq_svc, либо к __irq_usr. Просто прокрутите вниз, чтобы посмотреть на нижнюю часть entry-arm.S, которую вы уже нашли.

Связанный: Физический адрес векторной таблицы ARM-Linux

person artless noise    schedule 08.04.2014
comment
В этой версии mov r0, sp выполняется непосредственно перед movs pc, lr. Первый дает __irq_svc указатель на сохраненные три слова. 2-й меняет режимы. Макрос svc_entry использует r0 для перезагрузки значений. Поскольку прерывания отключены, это статическое хранилище используется только один раз. - person artless noise; 08.04.2014
comment
А, если кто не знает. ARM имеет разные стеки для каждого режима. Таким образом, sp_irq, sp_abt, sp_und и sp_svc — это разные регистры, указывающие на разные стеки. Linux использует только sp_svc, за исключением короткого периода, о котором спрашивает этот вопрос. - person artless noise; 08.04.2014