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 의 실패로 인해 재수행의 필요성에 따라
복귀시 재수행을 하게 된다는 것이다.