ARM Exception Vector

ARM에서 Exception이 발생하면 CPU는 지정된 벡터테이블(low/high)로 강제 jump되고 해당 엔트리에는 각 exception을 처리할 수 있는 코드로의 branch 코드가  수행된다.

 

아래 그림은 8개의 exception vector entry 중 5개 엔트리의 경우 각각 16개의 모드 테이블을 갖고 있고 exception 되기 전의 모드를 인덱스로 하여 해당 루틴으로 이동하는 것을 보여준다.

arm-exception-vector-1a

 

Vector 선언

__vectors_start

arch/arm/kernel/entry-armv.S

        .section .vectors, "ax", %progbits
__vectors_start:
        W(b)    vector_rst
        W(b)    vector_und
        W(ldr)  pc, __vectors_start + 0x1000
        W(b)    vector_pabt
        W(b)    vector_dabt
        W(b)    vector_addrexcptn
        W(b)    vector_irq
        W(b)    vector_fiq
  • 8개의 exception vector 엔트리에는 각각을 처리할 수 있는 stub 코드로 이동할 수 있는 branch 코드로 되어 있다.
  • 3번 째 엔트리의 경우 벡터위치+0x1000에 담겨있는 주소로 이동하는 코드가 있는데 해당 위치는 vector_swi 루틴을 담고 있다.

 

Vector가 저장되는 섹션 위치

        _etext = .;                     /* End of text and rodata section */

#ifndef CONFIG_XIP_KERNEL
# ifdef CONFIG_ARM_KERNMEM_PERMS
        . = ALIGN(1<<SECTION_SHIFT);
# else
        . = ALIGN(PAGE_SIZE);
# endif
        __init_begin = .;
#endif
        /*
         * The vectors and stubs are relocatable code, and the
         * only thing that matters is their relative offsets
         */
        __vectors_start = .;
        .vectors 0 : AT(__vectors_start) {
                *(.vectors)
        }
        . = __vectors_start + SIZEOF(.vectors);
        __vectors_end = .;

        __stubs_start = .;
        .stubs 0x1000 : AT(__stubs_start) {
                *(.stubs)
        }
        . = __stubs_start + SIZEOF(.stubs);
        __stubs_end = .;

        INIT_TEXT_SECTION(8)
        .exit.text : {
                ARM_EXIT_KEEP(EXIT_TEXT)
        }
  • 벡터 테이블의 원본 데이터는 _etext 다음인 .vector 섹션에 위치하고 stub 코드들은 .stubs 섹션에 위치한다.

 

Vector 설치

exception 벡터 및 stub 코드들은 아래의 루틴에서 설치된다.

  • paging_init()->devicemaps_init()->early_trap_init()

 

Vector 핸들러

 

vector_stub

arch/arm/kernel/entry-armv.S

/*
 * Vector stubs.
 *
 * This code is copied to 0xffff1000 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not exceed
 * a page size.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
        .macro  vector_stub, name, mode, correction=0
        .align  5

vector_\name:
        .if \correction
        sub     lr, lr, #\correction
        .endif

        @
        @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
        @ (parent CPSR)
        @
        stmia   sp, {r0, lr}            @ save r0, lr
        mrs     lr, spsr
        str     lr, [sp, #8]            @ save spsr

        @
        @ Prepare for SVC32 mode.  IRQs remain disabled.
        @
        mrs     r0, cpsr
        eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
        msr     spsr_cxsf, r0

        @
        @ the branch table must immediately follow this code
        @
        and     lr, lr, #0x0f
 THUMB( adr     r0, 1f                  )
 THUMB( ldr     lr, [r0, lr, lsl #2]    )
        mov     r0, sp
 ARM(   ldr     lr, [pc, lr, lsl #2]    )
        movs    pc, lr                  @ branch to handler in SVC mode
ENDPROC(vector_\name)

        .align 2
        @ handler addresses follow this label
1:
        .endm

Exception이 발생되면 8개의 exception vector 엔트리 중 해당하는 exception 위치로 jump 되는데 이 중 5개의 exception 엔트리에서 사용되는 공통 매크로 루틴에서는 exception 벡터로 점프되기 직전의 최대 16개 모드별 테이블에 해당하는 루틴을 찾아 수행한다. 수행 전에 svc 모드로 변환한다.

  • correction은 루틴이 수행된 후 다시 리턴될 주소를 조정한다. exception이 발생될 때의 파이프라인 위치에 따라 보정 주소가 다르다.
    • IRQ, FIQ, Data Abort의 경우 리턴 주소-4로 보정한다.
    • Prefetch Abort의 경우 리턴 주소-8로 보정한다.
    • Undefined 및 SWI의 경우 보정 없이 리턴 주소를 사용한다.
    • Reset은 리턴하지 않으므로 해당사항 없다.
  • irq, fiq, abt, und 모드에서 사용하는 스택
    • cpu_init() 함수에서 3 word 초소형 static 배열을 4개의 스택으로 설정하였다.
    • 이 스택에는 다음 3가지 항목을 보관한다.
      • scratch되어 파괴될 r0 레지스터
      • 되돌아갈 주소가 담긴 lr 레지스터(exception 시 pc -> lr_<exception>에 복사되는데 이 값에 correction 값을 보정하여 저장해둔다.)
      • 복원되어야 할 psr 레지스터 (exception 시 cpsr -> spsr_<exception>에 복사되는데 이 값을 저장해둔다.)
  • ldm/stm에서 사용하는 ^ 접미사 용도
    • stm
      • user 모드의 sp, lr 레지스터가 뱅크되어 일반적으로는 다른 모드에서  접근할 수 없어서 user 모드의 sp, lr 레지스터 값을 access하고자할 때 사용
    • ldm
      • pc가 포함된 경우 spsr->cpsr로 복사된다
      • 기존 모드로 복귀할 때 spsr(백업받아 두었던) -> cpsr로 복사한다.

 

.stubs 섹션의 시작

        .section .stubs, "ax", %progbits
__stubs_start:
        @ This must be the first word
        .word   vector_swi
  • 시스템 콜이 호출되는 vector_swi가 벡터 stub의 처음에 위치한다.

 

vector_rst (Reset)

vector_rst:
 ARM(   swi     SYS_ERROR0      )
 THUMB( svc     #0              )
 THUMB( nop                     )
        b       vector_und

SYS_ERROR0 소프트웨어 인터럽트를 발생시켜 시스템 콜을 호출하게 한다.

 

vector_irq (IRQ)

/*
 * Interrupt dispatcher
 */
        vector_stub     irq, IRQ_MODE, 4

        .long   __irq_usr                       @  0  (USR_26 / USR_32)
        .long   __irq_invalid                   @  1  (FIQ_26 / FIQ_32)
        .long   __irq_invalid                   @  2  (IRQ_26 / IRQ_32)
        .long   __irq_svc                       @  3  (SVC_26 / SVC_32)
        .long   __irq_invalid                   @  4
        .long   __irq_invalid                   @  5
        .long   __irq_invalid                   @  6
        .long   __irq_invalid                   @  7
        .long   __irq_invalid                   @  8
        .long   __irq_invalid                   @  9
        .long   __irq_invalid                   @  a
        .long   __irq_invalid                   @  b
        .long   __irq_invalid                   @  c
        .long   __irq_invalid                   @  d
        .long   __irq_invalid                   @  e
        .long   __irq_invalid                   @  f
  • USR 모드에서 인터럽트가 발생한 경우 __irq_usr 루틴을 호출한다.
  • FIQ 및 IRQ 모드에서 인터럽트가 발생한 경우 __irq_invalid 루틴을 호출한다.
  • SVC 모드에서 인터럽트가 발생한 경우 __irq_svc 루틴을 호출한다.

 

vector_dabt (Data Abort)

/*
 * Data abort dispatcher
 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
 */
        vector_stub     dabt, ABT_MODE, 8

        .long   __dabt_usr                      @  0  (USR_26 / USR_32)
        .long   __dabt_invalid                  @  1  (FIQ_26 / FIQ_32)
        .long   __dabt_invalid                  @  2  (IRQ_26 / IRQ_32)
        .long   __dabt_svc                      @  3  (SVC_26 / SVC_32)
        .long   __dabt_invalid                  @  4
        .long   __dabt_invalid                  @  5
        .long   __dabt_invalid                  @  6
        .long   __dabt_invalid                  @  7
        .long   __dabt_invalid                  @  8
        .long   __dabt_invalid                  @  9
        .long   __dabt_invalid                  @  a
        .long   __dabt_invalid                  @  b
        .long   __dabt_invalid                  @  c
        .long   __dabt_invalid                  @  d
        .long   __dabt_invalid                  @  e
        .long   __dabt_invalid                  @  f

 

vector_pabt (Prefetch Abort)

/*
 * Prefetch abort dispatcher
 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
 */
        vector_stub     pabt, ABT_MODE, 4

        .long   __pabt_usr                      @  0 (USR_26 / USR_32)
        .long   __pabt_invalid                  @  1 (FIQ_26 / FIQ_32)
        .long   __pabt_invalid                  @  2 (IRQ_26 / IRQ_32)
        .long   __pabt_svc                      @  3 (SVC_26 / SVC_32)
        .long   __pabt_invalid                  @  4
        .long   __pabt_invalid                  @  5
        .long   __pabt_invalid                  @  6
        .long   __pabt_invalid                  @  7
        .long   __pabt_invalid                  @  8
        .long   __pabt_invalid                  @  9
        .long   __pabt_invalid                  @  a
        .long   __pabt_invalid                  @  b
        .long   __pabt_invalid                  @  c
        .long   __pabt_invalid                  @  d
        .long   __pabt_invalid                  @  e
        .long   __pabt_invalid                  @  f

 

vector_und (Undefined Instruction)

/*
 * Undef instr entry dispatcher
 * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 */
        vector_stub     und, UND_MODE

        .long   __und_usr                       @  0 (USR_26 / USR_32)
        .long   __und_invalid                   @  1 (FIQ_26 / FIQ_32)
        .long   __und_invalid                   @  2 (IRQ_26 / IRQ_32)
        .long   __und_svc                       @  3 (SVC_26 / SVC_32)
        .long   __und_invalid                   @  4
        .long   __und_invalid                   @  5
        .long   __und_invalid                   @  6
        .long   __und_invalid                   @  7
        .long   __und_invalid                   @  8
        .long   __und_invalid                   @  9
        .long   __und_invalid                   @  a
        .long   __und_invalid                   @  b
        .long   __und_invalid                   @  c
        .long   __und_invalid                   @  d
        .long   __und_invalid                   @  e
        .long   __und_invalid                   @  f

        .align  5

 

vector_addrexcptn (Address Exception Handler)

/*=============================================================================
 * Address exception handler
 *-----------------------------------------------------------------------------
 * These aren't too critical.
 * (they're not supposed to happen, and won't happen in 32-bit data mode).
 */

vector_addrexcptn:
        b       vector_addrexcptn

 

vector_fiq (FIQ)

/*=============================================================================
 * FIQ "NMI" handler
 *-----------------------------------------------------------------------------
 * Handle a FIQ using the SVC stack allowing FIQ act like NMI on x86
 * systems.
 */
        vector_stub     fiq, FIQ_MODE, 4

        .long   __fiq_usr                       @  0  (USR_26 / USR_32)
        .long   __fiq_svc                       @  1  (FIQ_26 / FIQ_32)
        .long   __fiq_svc                       @  2  (IRQ_26 / IRQ_32)
        .long   __fiq_svc                       @  3  (SVC_26 / SVC_32)
        .long   __fiq_svc                       @  4
        .long   __fiq_svc                       @  5
        .long   __fiq_svc                       @  6
        .long   __fiq_abt                       @  7
        .long   __fiq_svc                       @  8
        .long   __fiq_svc                       @  9
        .long   __fiq_svc                       @  a
        .long   __fiq_svc                       @  b
        .long   __fiq_svc                       @  c
        .long   __fiq_svc                       @  d
        .long   __fiq_svc                       @  e
        .long   __fiq_svc                       @  f

        .globl  vector_fiq_offset
        .equ    vector_fiq_offset, vector_fiq

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.