강좌 & 팁
글 수 2,412
2011.06.12 01:22:22 (*.138.143.120)
43041
arm exception 으로 부터의 복귀
exception 의 복귀는 3가지로 분류된다.
case 1 : SWI 에 의한 분기로부터의 복귀
case 2 : IRQ, FIQ 나 prefetch abort 로 부터의 복귀
case 3 : data abort 로 부터의 복귀
세가지 모두 리턴주소를 의미하는 lr 을 실행주소인 pc 로 이동시켜서
exception 이 발생하기 이전의 코드를 실행시켜 주는 것이 목적이다.
여기에는 복구시에 이전 명령의 진행을 그대로 할수 있도록 해주기 위한
환경을 만들어주는 것이 필요한다.
먼저 아주 간단하게 exception 이 발생하고 난후 아무것도 하지 않고 그대로 복귀한다고 가정하자.
각 케이스별로 복귀명령을 확인하고 그안에 무엇이 있는지 확인해 보자
각 명령어의 s 가 붙는 이유는 exception 발생이전의 cpsr 을 복귀와 동시에 spsr 로부터
복사해서 복구시켜주는 명령이다.
기본적으로 exception 이 발생할때 cpsr 의 내용이 spsr 에 저장되기 때문이다.
case 1 : movs pc, lr
exception 분기시 갖고 있던 pc 의 위치로 복귀시킨다.
case 2 : subs pc, r14, #4
exception 발생시 수행해야할 코드를 수행해야 한다.
그 위치는 pc 가 가리키고 있는 위치의 직전이므로 -4 에 위치한다.
case 3 : subs pc, r14, #8
exception 발생의 원인이 data abort 에 해당하기 때문에
실행시는 이미 pc 가 +4 되어 있으므로 수행해야할 코드는 -8 위치에 있다.
case 1 의 경우는 간단하게 이해할수 있다.
하지만 case 2 와 case 3 의 경우는 미묘하게 다른 차이로 인해서 복귀주소가 다르게 된다.
정확하게 이해하기 위해서는 pc 가 언제 증가하는지 exception 발생시 분기가 언제일어나는지
명확하게 해둘 필요가 있다.
먼저 pc 는 언제 증가하는가?
물론 pc 는 다음번 명령이 수행할 위치를 가리키고 있으므로 현재 명령이 실행될때 증가한다.
좀더 기술적인 설명으로는 op code 가 실행되는 순간 pc 는 증가한다.
명령어는 다음과 같은 과정을 거쳐 실행된다.
FETCH -> DECODE -> EXECUTE
pc 의 증가는 EXECUTE 단계에서 수행된다.
case 2 의 IRQ, FIQ 의 분기는 EXECUTE 이 실행되고 나서 분기하게 된다.
그렇기 때문에 이번에 실행할 코드가 수행되지 못하고 분기하였고 pc 는 그 다음주소를 가리키므로
복귀시에 pc - 4 의 위치가 복귀 위치가 된다.
그럼 case 3 의 분기는 언제 일어나는가?
case 2 와 마찬가지로 EXECUTE 수행과 동시에 일어나게 된다.
하지만 EXECUTE 을 실행할때 잘못된 공간으로의 접근으로 인해서 data abort 가 발생하게 되고
data abort 의 처리가 정상적인 처리가 된 경우 복귀 주소는 pc - 4 가 아니라
EXECUTE 실행에서 문제가 되었으므로 해당 EXECUTE 을 다시 실행해야 하는 문제가 생긴 것이다.
따라서 복귀 주소는 pc - 8 로 결정된다.
그럼 arm 에서는 이런 부분을 어떻게 구현했는지 살펴보자.
linux-2.6.21/arch/arm/kernel/entry-armv.S 파일에는 인터럽트 벡터의 처리에 관한 매크로들이 있다.
그중에서 볼 부분은 vector_stub 라는 매크로이다.
883 /*
884 * Vector stubs.
885 *
886 * This code is copied to 0xffff0200 so we can use branches in the
887 * vectors, rather than ldr's. Note that this code must not
888 * exceed 0x300 bytes.
889 *
890 * Common stub entry macro:
891 * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
892 *
893 * SP points to a minimal amount of processor-private memory, the address
894 * of which is copied into r0 for the mode specific abort handler.
895 */
896 .macro vector_stub, name, mode, correction=0
897 .align 5
898
899 vector_\name:
900 .if \correction
901 sub lr, lr, #\correction
902 .endif
903
904 @
905 @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
906 @ (parent CPSR)
907 @
908 stmia sp, {r0, lr} @ save r0, lr
909 mrs lr, spsr
910 str lr, [sp, #8] @ save spsr
911
912 @
913 @ Prepare for SVC32 mode. IRQs remain disabled.
914 @
915 mrs r0, cpsr
916 eor r0, r0, #(\mode ^ SVC_MODE)
917 msr spsr_cxsf, r0
918
919 @
920 @ the branch table must immediately follow this code
921 @
922 and lr, lr, #0x0f
923 mov r0, sp
924 ldr lr, [pc, lr, lsl #2]
925 movs pc, lr @ branch to handler in SVC mode
926 .endm
928 .globl __stubs_start
929 __stubs_start:
930 /*
931 * Interrupt dispatcher
932 */
933 vector_stub irq, IRQ_MODE, 4
952 /*
953 * Data abort dispatcher
954 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
955 */
956 vector_stub dabt, ABT_MODE, 8
975 /*
976 * Prefetch abort dispatcher
977 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
978 */
979 vector_stub pabt, ABT_MODE, 4
998 /*
999 * Undef instr entry dispatcher
1000 * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
1001 */
1002 vector_stub und, UND_MODE
vector_stub 라는 매크로는 인터럽트의 종류에 따라 복귀 주소를 4 혹은 8 혹은 0 으로 pc - #n
해서 lr 에 미리 저장하고 복귀시에는 단순히 movs pc, lr 을 사용한다.
중요한 점은 pc 의 증가는 op code 의 실행과 동시에 증가한다는것.
exception 은 현재의 실행사이클이 수행된후 분기하는 것이며,
irq, fiq 와 다르게 data abort 은 해당 excute 의 실패로 인해 재수행의 필요성에 따라
복귀시 재수행을 하게 된다는 것이다.