我发现这些关键部分存在一些潜在的问题。所有这些都有一些警告和解决方案,但总结如下:
- 出于优化或其他随机原因,没有什么可以阻止编译器在这些宏之间移动代码。
- 它们保存并恢复处理器状态的某些部分,编译器希望它们可以内联汇编保持独立(除非另有说明)。
- 没有什么可以阻止在序列的中间发生中断以及在读取和写入之间改变状态的。
首先,您肯定需要一些编译器内存障碍。GCC将这些实现为掩饰。基本上,这是一种告诉编译器的方法:“不,您不能在此内联汇编程序之间移动内存访问,因为它可能会影响内存访问的结果。” 具体来说,在开始和结束宏上都需要"memory"
和"cc"
clobbers。这些也将防止其他事情(例如函数调用)相对于内联程序集重新排序,因为编译器知道它们可能具有内存访问权限。我已经看到使用"memory"
Clobber的内联汇编中的条件代码寄存器中的GCC for ARM保持状态,所以您确实需要"cc"
。
其次,这些关键部分所节省和恢复的内容不仅仅是中断是否被允许。具体来说,他们正在保存和恢复大多数CPSR(当前程序状态寄存器)(该链接用于Cortex-R4,因为我找不到适合A9的图表,但应该相同)。有细微的限制实际上可以修改状态的部分,,但是在这里,这超出了必要。
除其他外,这包括条件代码(将类似指令的结果cmp
存储在其中,以便后续的条件指令可以作用于结果)。编译器一定会对此感到困惑。使用以下方法可以轻松解决"cc"
如上所述隔板。但是,这会使代码每次都失败,所以听起来好像并没有出现问题。但是,这有点像定时炸弹,因为修改随机的其他代码可能会导致编译器做一些稍有不同的操作,这将因此而被破坏。
这还将尝试保存/恢复用于实现Thumb条件执行的IT位。请注意,如果您从不执行Thumb代码,则无关紧要。除了得出结论,我从未弄清楚GCC的内联汇编如何处理IT位,这意味着编译器绝不能将内联汇编放入IT块中,并且始终希望该汇编结束于IT块之外。我从未见过GCC会生成违反这些假设的代码,并且我已经进行了一些相当复杂的内联汇编并进行了严格的优化,因此我可以肯定地确定它们成立了。这意味着它实际上可能不会尝试更改IT位,在这种情况下,一切都很好。尝试修改这些位为分类为“架构上不可预测”,因此它可能会做各种坏事,但可能根本不会做任何事。
模式位是将要保存/恢复的最后一类位(除了那些实际上禁止中断的位)。这些可能不会更改,因此可能无关紧要,但是,如果您有任何代码故意更改模式,则这些中断部分可能会导致问题。我期望只有在特权模式和用户模式之间进行切换。
第三,没有什么可以阻止中断在MRS
和MSR
中之间更改CPSR的其他部分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
因为至少某些GCC版本(例如我的)默认为不支持cpsie
and 的旧版ARM CPU 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