그 중요한 부분에 몇 가지 잠재적 인 문제가 있습니다. 이 모든 것에 대한주의 사항과 해결책이 있지만 요약하면 다음과 같습니다.
- 최적화 또는 임의의 다른 이유로 컴파일러가 이러한 매크로를 통해 코드를 이동하는 것을 막는 것은 없습니다.
- 컴파일러는 인라인 어셈블리가 따로 언급되지 않는 한 프로세서 상태의 일부를 저장하고 복원합니다.
- 시퀀스 중간에 인터럽트가 발생하는 것을 막고 읽을 때와 쓰는 시점 사이의 상태를 변경하는 것은 없습니다.
먼저 컴파일러 메모리 장벽이 필요합니다 . GCC는이를 클로버 로 구현합니다 . 기본적으로 이것은 컴파일러에게 "아니요. 메모리 액세스 결과에 영향을 줄 수 있기 때문에이 인라인 어셈블리에서 메모리 액세스를 이동할 수 없습니다"라고 말하는 방법입니다. 특히, 둘 필요 "memory"
하고 "cc"
(가) 시작과 끝 매크로 모두에서 내리 쳤을를. 이것들은 컴파일러가 메모리 액세스를 가질 수 있다는 것을 알고 있기 때문에 함수 호출과 같은 다른 것들이 인라인 어셈블리와 관련하여 재정렬되는 것을 방지합니다. "memory"
클로버가있는 인라인 어셈블리의 조건 코드 레지스터에서 ARM 유지 상태에 대한 GCC를 보았 으므로 클로버가 반드시 필요합니다 "cc"
.
둘째, 이러한 중요한 섹션은 인터럽트 활성화 여부보다 훨씬 더 많은 것을 저장하고 복원합니다. 특히, 그들은 대부분의 CPSR (현재 프로그램 상태 레지스터)을 저장하고 복원 하고 있습니다 (링크는 A9의 멋진 다이어그램을 찾을 수 없기 때문에 Cortex-R4에 대한 링크이지만 동일해야합니다). 실제로 상태를 수정할 수있는 부분 에는 미묘한 제한 이 있지만 여기서 필요한 것 이상입니다.
무엇보다도 여기에는 조건 코드가 포함됩니다 ( cmp
다음 과 같은 조건부 명령이 결과에 작용할 수 있도록 명령 결과 가 저장 되는 위치 ). 컴파일러는 이것에 의해 혼동 될 것입니다. "cc"
위에서 언급 한 클로버를 사용하면 쉽게 해결할 수 있습니다 . 그러나 이렇게하면 코드가 실패 할 때마다 문제가 발생하는 것처럼 들리지 않습니다. 그러나 임의의 다른 코드를 수정하면 컴파일러가 약간 다른 작업을 수행하여 이로 인해 깨질 수있는 시한 폭탄이 다소 있습니다.
또한 Thumb 조건부 실행 을 구현 하는 데 사용되는 IT 비트를 저장 / 복원하려고 시도합니다 . Thumb 코드를 실행하지 않으면 중요하지 않습니다. 필자는 GCC의 인라인 어셈블리가 IT 비트를 처리하는 것 외에 IT 비트를 처리하는 방법을 찾지 못했습니다. 컴파일러는 IT 블록에 인라인 어셈블리를 배치해서는 안되며 어셈블리가 항상 IT 블록 외부에서 종료 될 것으로 예상합니다. 나는 GCC가 이러한 가정을 위반하는 코드를 생성하는 것을 본 적이 없으며, 최적화가 엄격한 상당히 복잡한 인라인 어셈블리를 수행 했으므로 합리적으로 확신합니다. 이것은 실제로 IT 비트를 변경하려고 시도하지 않을 것이며,이 경우 모든 것이 정상입니다. 이 비트를 수정하려고하면 "건축 적으로 예측할 수없는" 것으로 분류됩니다따라서 모든 종류의 나쁜 일을 할 수는 있지만 아무것도하지 않을 것입니다.
저장 / 복원 될 비트의 마지막 범주 (실제로 인터럽트를 비활성화하는 것 외에)는 비트입니다. 이것들은 변경되지 않을 것이므로 아마도 중요하지 않을 것입니다. 그러나 의도적으로 모드를 변경하는 코드가 있으면 이러한 인터럽트 섹션이 문제를 일으킬 수 있습니다. 권한 모드와 사용자 모드 사이를 변경하는 것이 내가 기대하는 유일한 경우입니다.
셋째, 사이 CPSR의 다른 부분 변경하는 인터럽트를 방지 아무것도 없다 MRS
와 MSR
의를 ARM_INT_LOCK
. 이러한 변경 사항을 덮어 쓸 수 있습니다. 가장 합리적인 시스템에서 비동기 인터럽트는 CPSR을 포함하여 인터럽트하는 코드의 상태를 변경하지 않습니다. 그렇게하면 코드가 무엇을할지 추론하기가 매우 어려워집니다. 그러나 (FIQ 비활성화 비트 변경이 가장 가능성이 높은 것 같습니다) 시스템에서이 작업을 수행하는지 고려해야합니다.
다음은 내가 지적한 모든 잠재적 문제를 해결하는 방식으로 구현하는 방법입니다.
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
컴파일해야합니다 -mcpu=cortex-a9
지원하지 않는 구형 ARM의 CPU로 인해 적어도 일부 GCC 버전 (광산 등) 기본 cpsie
및 cpsid
.
내가 사용하는 ands
대신의 and
에서 ARM_INT_LOCK
가이 Thumb 코드에서 사용될 경우 16 비트 명령어 그래서. "cc"
엄격히 성능 / 코드 크기 혜택 그래서 소지품은, 어쨌든 필요하다.
0
하고 1
있는 지역 레이블 참조가.
이들은 버전과 동일한 방식으로 사용할 수 있어야합니다. 은 ARM_INT_LOCK
단지 빨리 / 작은 원래 하나입니다. 불행히도, 나는 ARM_INT_UNLOCK
몇 가지 지시 사항 근처에서 안전하게 할 수 있는 방법을 생각해 낼 수 없었습니다 .
IRQ 및 FIQ 비활성화시 시스템에 제약이있는 경우 단순화 할 수 있습니다. 예를 들어, 항상 함께 사용 중지 된 경우 다음과 같이 하나의 cbz
+ 로 결합 할 수 있습니다 cpsie if
.
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
또는 FIQ를 전혀 신경 쓰지 않으면 완전히 활성화 / 비활성화하는 것과 비슷합니다.
당신이 다른 아무 것도 알지 못하는 경우 그때 당신은 또한 사용은 모두 제외하고, 원래의 코드와 매우 비슷한 계속 수, 잠금 및 잠금 해제 사이 CPSR에서 다른 상태 비트의 변경 "memory"
및 "cc"
내리 쳤을 때 모두 ARM_INT_LOCK
와ARM_INT_UNLOCK