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, ®s->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 <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자

최근 덧글