Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

8179
1390
307630


[ARM프로세서] XEN: 익셉션 벡터 테이블(ARMv7) 익셉션(Exception) 소개

XEN 하이퍼바이저의 익셉션 벡터 테이블을 살펴봅시다.

익셉션 벡터 테이블의 선언부

다음은 XEN 하이퍼바이저의 익셉션 벡터 테이블의 선언부입니다.

xen/xen/arch/arm/arm32/entry.S
        vect_br 0, trap_fiq
        vect_br 1, trap_irq
        vect_br 2, trap_guest_sync
        vect_br 3, trap_data_abort
        vect_br 4, trap_prefetch_abort
        vect_br 5, trap_hypervisor_call
        vect_br 6, trap_undefined_instruction
        vect_br 7, trap_reset

위 코드가 컴파일되면 다음과 같은 명령어를 볼 수 있습니다.

01 NSR:00267980|EA000040        hyp_traps_vector:   b       0x267A88         ; trap_reset
02 NSR:00267984|EA00005D     b       0x267B00         ; trap_undefined_instruction  // Undefined Instruction, from Hyp mode
03 NSR:00267988|EA00007A     b       0x267B78         ; trap_hypervisor_call // Hypervisor Call, from Hyp mode
04 NSR:0026798C|EA000097     b       0x267BF0         ; trap_prefetch_abort // Prefetch Abort, from Hyp mode
05 NSR:00267990|EA0000B4     b       0x267C68         ; trap_data_abort // Data Abort, from Hyp mode
06 NSR:00267994|EA0000D1     b       0x267CE0         ; trap_guest_sync // Hyp Trap, or Hyp mode entryc
07 NSR:00267998|EA0000EF     b       0x267D5C         ; trap_irq // IRQ interrupt
08 NSR:0026799C|EA00010D     b       0x267DD8         ; trap_fiq   // FIQ interrupt

익셉션 벡터 테이블의 베이스 주소는 00267980이고 0x4 바이트 단위로 익셉션 벡터가 정렬됐음을 알 수 있습니다. 

코드 분석

이어서 코드를 분석해봅시다. 먼저 02번째 줄을 보겠습니다.

02 NSR:00267984|EA00005D     b       0x267B00         ; trap_undefined_instruction  // Undefined Instruction, from Hyp mode

Hyp 모드에서 Undefined 익셉션이 발생하면 실행됩니다.

03번째 줄을 이어서 봅시다.

03 NSR:00267988|EA00007A     b       0x267B78         ; trap_hypervisor_call // Hypervisor Call, from Hyp mode

Hyp 모드에서 하이퍼바이저 콜을 실행하면 브랜치되는 주소입니다. 하이퍼바이저 콜에 해당하는 명령어는 'hvc'입니다.

이어서 04~05번째 줄입니다.

04 NSR:0026798C|EA000097     b       0x267BF0         ; trap_prefetch_abort // Prefetch Abort, from Hyp mode
05 NSR:00267990|EA0000B4     b       0x267C68         ; trap_data_abort // Data Abort, from Hyp mode

Hyp 모드에서 프리페치 어보트와 데이터 어보트가 발생하면 브랜치되는 주소입니다.

06번째 줄을 보겠습니다. 하이퍼바이저 관점으로 매우 중요한 코드입니다.

06 NSR:00267994|EA0000D1     b       0x267CE0         ; trap_guest_sync // Hyp Trap, or Hyp mode entryc

슈퍼바이저 모드(커널)에서 'hvc'와 같은 명령어를 실행하거나 Guest Exit를 수행할 때 브랜치되는 주소입니다.
게스트 OS와 하이퍼바이저의 인터페이스라 볼 수 있습니다.

마지막으로 07~08번째 줄을 보겠습니다.

07 NSR:00267998|EA0000EF     b       0x267D5C         ; trap_irq // IRQ interrupt
08 NSR:0026799C|EA00010D     b       0x267DD8         ; trap_fiq   // FIQ interrupt

hyp 모드에서 IRQ와 FIQ 익셉션이 발생하면 브랜치되는 코드입니다.


게스트 Exit 관련 코드


trap_guest_sync 레이블과 do_trap_guest_sync() 함수가 호출되는데, XEN 하이퍼바이저에서 가장 중요한 코드 중 하나입니다.
각각 코드의 구현부를 소개합니다.

   NSR:00267CE0|E24DD014  trap_guest_sync:   sub     r13,r13,#0x14    ; r13,r13,#20
   NSR:00267CE4|E92D1FFF                     push    {r0-r12}
   NSR:00267CE8|E10EB300                     mrs     r11,elr_hyp
   NSR:00267CEC|E58DB03C                     str     r11,[r13,#0x3C]
   NSR:00267CF0|E58DE038                     str     r14,[r13,#0x38]
   NSR:00267CF4|E28DB044                     add     r11,r13,#0x44    ; r11,r13,#68
   NSR:00267CF8|E58DB034                     str     r11,[r13,#0x34]
   NSR:00267CFC|EE95BF12                     mrc     p15,0x4,r11,c5,c2,0x0   ; p15,4,r11,c5,c2,0 (hypervisor syndrome)
   NSR:00267D00|E58DB044                     str     r11,[r13,#0x44]
   NSR:00267D04|E14EB300                     mrs     r11,spsr_hyp
   NSR:00267D08|E58DB040                     str     r11,[r13,#0x40]
   NSR:00267D0C|E20B001F                     and     r0,r11,#0x1F     ; r0,r11,#31
   NSR:00267D10|E350001A                     cmp     r0,#0x1A         ; r0,#26
   NSR:00267D14|1A000005                     bne     0x267D30
   NSR:00267D18|E10FA000                     mrs     r10,cpsr
   NSR:00267D1C|E3CAA000                     bic     r10,r10,#0x0     ; r10,r10,#0
   NSR:00267D20|E20BB000                     and     r11,r11,#0x0     ; r11,r11,#0
   NSR:00267D24|E18AA00B                     orr     r10,r10,r11
   NSR:00267D28|E123F00A                     msr     cpsr_cx,r10
   NSR:00267D2C|EA000005                     b       0x267D48
   NSR:00267D30|EBFFFED2                     bl      0x267880         ; prepare_context_from_guest
   NSR:00267D34|F1080180                     cpsie   ai
   NSR:00267D38|E28FE008                     adr     r14,0x267D48
   NSR:00267D3C|E3540000                     cmp     r4,#0x0          ; r4,#0
   NSR:00267D40|128FEF42                     adrne   r14,0x267E50     ; r14,return_from_trap
   NSR:00267D44|EAFFE224                     b       0x2605DC         ; enter_hypervisor_from_guest
   NSR:00267D48|E28FEC01                     adr     r14,0x267E50     ; r14,return_from_trap
   NSR:00267D4C|E1A0000D                     cpy     r0,r13
   NSR:00267D50|E1A0B00D                     cpy     r11,r13
   NSR:00267D54|E3CDD007                     bic     r13,r13,#0x7     ; r13,r13,#7
   NSR:00267D58|EAFFE229                     b       0x260604         ; do_trap_guest_sync

xen/xen/arch/arm/traps.c
void do_trap_guest_sync(struct cpu_user_regs *regs)
{
    const union hsr hsr = { .bits = regs->hsr };

    switch ( hsr.ec )
    {
    case HSR_EC_WFI_WFE:
        /*
         * HCR_EL2.TWI, HCR_EL2.TWE
         *
         * ARMv7 (DDI 0406C.b): B1.14.9
         * ARMv8 (DDI 0487A.d): D1-1505 Table D1-51
         */
        if ( !check_conditional_instr(regs, hsr) )
        {
            advance_pc(regs, hsr);
            return;
        }
        if ( hsr.wfi_wfe.ti ) {
            /* Yield the VCPU for WFE */
            perfc_incr(trap_wfe);
            vcpu_yield();
        } else {
            /* Block the VCPU for WFI */
            perfc_incr(trap_wfi);
            vcpu_block_unless_event_pending(current);
        }
        advance_pc(regs, hsr);
        break;
    case HSR_EC_CP15_32:
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_cp15_32);
        do_cp15_32(regs, hsr);
        break;
    case HSR_EC_CP15_64:
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_cp15_64);
        do_cp15_64(regs, hsr);
        break;
    case HSR_EC_CP14_32:
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_cp14_32);
        do_cp14_32(regs, hsr);
        break;
    case HSR_EC_CP14_64:
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_cp14_64);
        do_cp14_64(regs, hsr);
        break;
    case HSR_EC_CP14_DBG:
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_cp14_dbg);
        do_cp14_dbg(regs, hsr);
        break;
    case HSR_EC_CP:
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_cp);
        do_cp(regs, hsr);
        break;
    case HSR_EC_SMC32:
        /*
         * HCR_EL2.TSC
         *
         * ARMv7 (DDI 0406C.b): B1.14.8
         * ARMv8 (DDI 0487A.d): D1-1501 Table D1-44
         */
        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_smc32);
        do_trap_smc(regs, hsr);
        break;
    case HSR_EC_HVC32:
    {
        register_t nr;

        GUEST_BUG_ON(!psr_mode_is_32bit(regs));
        perfc_incr(trap_hvc32);
#ifndef NDEBUG
        if ( (hsr.iss & 0xff00) == 0xff00 )
            return do_debug_trap(regs, hsr.iss & 0x00ff);
#endif
        if ( hsr.iss == 0 )
            return do_trap_hvc_smccc(regs);
        nr = regs->r12;
        do_trap_hypercall(regs, &nr, hsr);
        regs->r12 = (uint32_t)nr;
        break;
    }
#ifdef CONFIG_ARM_64
    case HSR_EC_HVC64:
        GUEST_BUG_ON(psr_mode_is_32bit(regs));
        perfc_incr(trap_hvc64);
#ifndef NDEBUG
        if ( (hsr.iss & 0xff00) == 0xff00 )
            return do_debug_trap(regs, hsr.iss & 0x00ff);
#endif
        if ( hsr.iss == 0 )
            return do_trap_hvc_smccc(regs);
        do_trap_hypercall(regs, &regs->x16, hsr);
        break;
    case HSR_EC_SMC64:
        /*
         * HCR_EL2.TSC
         *
         * ARMv8 (DDI 0487A.d): D1-1501 Table D1-44
         */
        GUEST_BUG_ON(psr_mode_is_32bit(regs));
        perfc_incr(trap_smc64);
        do_trap_smc(regs, hsr);
        break;
    case HSR_EC_SYSREG:
        GUEST_BUG_ON(psr_mode_is_32bit(regs));
        perfc_incr(trap_sysreg);
        do_sysreg(regs, hsr);
        break;
#endif

    case HSR_EC_INSTR_ABORT_LOWER_EL:
        perfc_incr(trap_iabt);
        do_trap_stage2_abort_guest(regs, hsr);
        break;
    case HSR_EC_DATA_ABORT_LOWER_EL:
        perfc_incr(trap_dabt);
        do_trap_stage2_abort_guest(regs, hsr);
        break;

    default:
        gprintk(XENLOG_WARNING,
                "Unknown Guest Trap. HSR=0x%x EC=0x%x IL=%x Syndrome=0x%"PRIx32"\n",
                hsr.bits, hsr.ec, hsr.len, hsr.iss);
        inject_undef_exception(regs, hsr);
    }
}

Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자




덧글

댓글 입력 영역