ARM64 기반 베어메탈 하이퍼바이저를 개발하던 중, CTR_EL0 인터셉트를 활성화하자 기기가 무작위로 멈추고 재시작되는 문제가 발생했습니다. 이 현상은 하이퍼바이저의 핵심 기능과 직결되어 해결이 필수적이었으나, 반년 동안 원인을 찾지 못해 개발에 난항을 겪었습니다. 초기에는 예외 처리기의 레지스터 손상이나 캐시 비일관성 등 다양한 가능성을 검토했지만, 문제의 본질은 다른 곳에 있었습니다.
문제의 원인은 CPU의 분기 예측(branch prediction) 기능과 관련이 있었습니다. 간접 호출(indirect call)인 `blr` 명령어가 잘못된 분기 대상을 예측하여, 잠긴 부트로더(bootrom) 영역인 0x0 주소에서 추측성으로 명령어를 인출(instruction fetch)하려 시도한 것이었습니다. 이 부트로더 영역은 MMIO(Memory-Mapped I/O)로 매핑되어 있었고, 실행 가능한(executable) 상태로 설정되어 있어 CPU가 추측성으로 명령어를 가져오려다 시스템이 멈춘 것입니다. ARM 아키텍처에서 메모리를 'Device' 속성으로 지정하면 추측성 데이터 접근만 차단되며, 추측성 명령어 접근까지 막으려면 해당 영역을 반드시 비실행(non-executable)으로 표시해야 합니다.
결론적으로, MMIO 페이지에 NX(No-Execute) 비트를 적용하여 해당 영역을 비실행으로 표시하자, 커널 패치 없이도 안드로이드(Android)가 완전히 부팅되는 데 성공했습니다. 이는 NX 비트가 단순히 스택 오버플로(stack overflow) 같은 보안 공격을 방어하는 DEP(Data Execution Prevention) 기능을 넘어, ARM 아키텍처에서 추측성 명령어 접근을 제어하고 하드웨어의 안정적인 동작을 보장하는 필수적인 메모리 속성임을 명확히 보여줍니다. 이번 사례는 메모리 실행 권한이 단순한 보안 기능을 넘어 시스템의 기능적 안정성에 직접적인 영향을 미친다는 중요한 교훈을 남겼습니다.