trap 상태 전이: 처리기 주소만 맞아서는 복귀할 수 없다
예외 처리기 주소로 PC를 바꿨는데, 처리 뒤 원래 프로그램으로 돌아오지 못했다. 되돌아갈 명령 주소와 이전 실행 권한을 저장하지 않았기 때문이다. trap 상태 전이는 일반 분기보다 더 많은 상태를 바꾼다. 처리기로 들어가는 순간 무엇을 기록하고 무엇을 막는지, 복귀 때 어떤 상태를 되돌리는지 함께 추적해야 한다.
ECALL 한 개가 바꾸는 상태
기본적인 RISC-V 환경에서 U-mode 프로그램이 주소 0x104의 32비트 ECALL을 실행한다고 하자. 해당 예외는 S-mode로 위임하지 않고 M-mode에서 처리하며, mtvec의 BASE는 0x1000이다. 가상 예제는 추가적인 double-trap 확장 등의 동작을 제외한다. 진입 전 mstatus의 MIE 값은 1로 둔다.
ECALL은 지원 실행 환경에 요청하기 위한 명령이다. U-mode에서 발생하는 원인 코드는 environment-call-from-U-mode인 8이다. M-mode 진입 때 핵심 상태는 다음처럼 변한다.
| 상태 | trap 진입 뒤 값 | 의미 |
|---|---|---|
| PC | 0x1000 | 처리기 진입 주소 |
| mepc | 0x104 | ECALL 명령 자체의 주소 |
| mcause | interrupt bit=0, code=8 | 동기 예외의 원인 |
| 현재 privilege | M | 처리기의 실행 권한 |
| MPP | U | 이전 실행 권한 |
| MPIE | 1 | 진입 직전 MIE의 값 |
| MIE | 0 | M-mode 인터럽트의 전역 허용 상태 |
원문은 ECALL의 epc가 다음 명령이 아니라 ECALL 자신의 주소라고 규정한다. 또 trap 진입에서 이전 권한과 인터럽트 허용 상태를 저장하고, 진입한 권한의 전역 인터럽트를 끈다. RISC-V Machine-Level ISA, Privilege and Global Interrupt-Enable Stack 및 Environment Call
MPIE가 1인 것은 U-mode 프로그램에 M-mode의 MIE가 적용되어 있었다는 뜻은 아니다. 더 높은 권한의 인터럽트는 낮은 권한에서 그 전역 비트와 다르게 허용 조건을 갖는다. 예제 표는 MIE 필드의 저장 전이를 보여 주며 모든 인터럽트 종류를 차단한다는 선언은 아니다.
Vectored여도 동기 예외는 BASE로 간다
mtvec가 Direct 모드이면 M-mode trap은 BASE로 들어간다. Vectored 모드에서도 동기 예외인 ECALL은 BASE=0x1000으로 들어간다. code=8을 사용해 0x1000+4×8=0x1020으로 보내면 안 된다. BASE+4×cause 규칙은 해당 모드의 비동기 인터럽트에 적용된다. RISC-V Machine-Level ISA, Machine Trap-Vector Base-Address Register
비교를 위해 같은 BASE에서 machine timer interrupt의 cause=7이 선택되었다면, Vectored 진입 주소는 0x101C다. 이 경우 mcause의 interrupt bit도 1이다. 단순히 원인 번호 하나만 보는 decoder로는 두 전이를 정확하게 나눌 수 없다.
mepc와 mcause 역시 서로 대체하지 않는다. mepc는 돌아갈 흐름의 위치이고 mcause는 왜 들어왔는지 설명한다. mtval은 예외별 보조 정보를 담거나 0일 수 있으며, 모든 trap에서 언제나 주소를 주는 레지스터가 아니다. 구현과 플랫폼이 제공하는 정보 규정을 확인해야 한다.
PC 저장만으로 프로그램 문맥을 보존하지는 못한다
하드웨어의 이 진입 규칙이 정수 레지스터 전체를 자동으로 메모리 stack에 저장하는 것은 아니다. 처리기가 t0나 a0를 사용하면 원래 프로그램이 필요로 하던 값을 보존할 책임은 처리기와 실행 환경에 있다. trap 발생 위치가 일반 함수 호출 지점이 아니므로 평소 호출 규약만으로 모든 보존 요구가 해결된다고 생각할 수 없다.
중첩 trap도 중요하다. mepc와 mcause, 상태의 저장 필드는 자동으로 무한한 이력 stack을 만들지 않는다. 처리기가 핵심 정보를 보존하기 전에 인터럽트를 다시 허용하거나 또 예외를 만들면, 추가 진입이 첫 trap의 복귀 정보를 덮을 수 있다. 원문은 이 초기 구간의 보호를 강조한다.
그래서 trap 전이는 ‘처리기 주소가 맞는가’와 함께 ‘복귀에 필요한 상태가 살아 있는가’로 확인해야 한다. 정수 레지스터 저장 여부는 소프트웨어 문맥 보존이고, mepc·권한·허용 비트의 전이는 하드웨어의 architectural 계약이다.
복귀 위치는 처리기가 결정할 수 있다
예제 처리기가 ECALL 요청을 완료하고 다음 명령부터 진행시키려면, 알고 있는 32비트 ECALL의 mepc에 4를 더해 0x108로 갱신할 수 있다. MRET은 이 mepc를 PC로 쓰고 MPP에 저장된 U-mode로 돌아간다. 기본 상태 전이에서 MIE는 MPIE의 1로 복원되고 MPIE는 1이 된다. RISC-V Machine-Level ISA, Trap-Return Instructions 및 mstatus의 복귀 규칙
mepc를 그대로 두면 다시 0x104의 ECALL로 돌아간다. 반대로 모든 예외에서 4를 더하는 규칙도 틀린다. 실패한 명령을 재시도해야 할 수 있고, 명령 길이가 다른 경우도 있다. 어떤 복귀 위치를 선택할지는 처리한 원인과 명령 의미에 달렸다.
trap 상태 전이는 원인·중단 위치·실행 권한·인터럽트 상태를 함께 기록하는 진입 계약이다. PC가 0x1000으로 향하는 것만 맞아서는 부족하다. 이 예제에서 0x108로 정상 복귀하려면 올바른 진입 기록, 처리기 문맥 보존, 의도한 mepc 변경과 MRET의 상태 복원이 연결되어야 한다.