为什么我的 ARM LDREX/STREX C 函数不起作用?
Why doesn't my ARM LDREX/STREX C function work?
根据“Barrier Litmus Tests and Cookbook”文档,我用 C 编写了一个 claim_lock 函数。我检查了生成的代码,一切看起来都不错,但是没有用。
// This code conforms to the section 7.2 of PRD03-GENC-007826:
// "Acquiring and Releasing a Lock"
static inline void claim_lock( uint32_t volatile *lock )
{
uint32_t failed = 1;
uint32_t value;
while (failed) {
asm volatile ( "ldrex %[value], [%[lock]]"
: [value] "=&r" (value)
: [lock] "r" (lock) );
if (value == 0) {
// The failed and lock registers are not allowed to be the same, so
// pretend to gcc that the lock pointer may be written as well as read.
asm volatile ( "strex %[failed], %[value], [%[lock]]"
: [failed] "=&r" (failed)
, [lock] "+r" (lock)
: [value] "r" (1) );
}
else {
asm ( "clrex" );
}
}
asm ( "dmb sy" );
}
生成的代码 (gcc):
1000: e3a03001 mov r3, #1
1004: e1902f9f ldrex r2, [r0]
1008: e3520000 cmp r2, #0
100c: 1a000004 bne 1024 <claim_lock+0x24>
1010: e1802f93 strex r2, r3, [r0]
1014: e3520000 cmp r2, #0
1018: 1afffff9 bne 1004 <claim_lock+0x4>
101c: f57ff05f dmb sy
1020: e12fff1e bx lr
1024: f57ff01f clrex
1028: eafffff5 b 1004 <claim_lock+0x4>
对应的释放函数:
static inline void release_lock( uint32_t volatile *lock )
{
// Ensure that any changes made while holding the lock are
// visible before the lock is seen to have been released
asm ( "dmb sy" );
*lock = 0;
}
它在 QEMU 中工作,但要么挂起,要么允许所有内核在真实硬件(Raspberry Pi 3 Cortex-A53)上“声明”所谓的“锁定”。
如果:
LDREX指令会挂掉核心(除非我的测试没有报异常)
- 未启用 MMU
- 包含锁的虚拟内存区域没有被缓存
如果出现以下情况,核心似乎会忽略彼此的主张:
- 尚未启用对称多处理
SMP 启用机制似乎因设备而异;检查特定内核的 TRM,它在 ARM ARM 的范围之外。
对于 Cortex-A53,要设置的位是 SMPEN,CPU 扩展控制寄存器的第 6 位,CPUECTLR。
较早的设备具有辅助控制寄存器的第 5 位,例如 (ARM11 MPcore),其中还需要考虑 SCU。我没有这样的设备,但正是我第一次注意到 SMP/nAMP 位的文档。
这是我在 ARMv7-M 架构的上下文切换部分找到的
参考手册
Blockquote
需要保证本地监视器在上下文切换后处于Open Access状态。在
ARMv7-M,本地监视器作为异常进入或退出的一部分自动更改为 Open Access
顺序。本地监视器也可以通过 CLREX 指令强制进入开放访问状态。
笔记
上下文切换不是应用程序级别的操作。但是,此信息包含在此处以
完成独占操作的描述。
上下文切换可能会导致后续 Store-Exclusive 失败,需要加载...存储序列
重播。为尽量减少发生这种情况的可能性,ARM 建议 Store-Exclusive
指令尽可能靠近关联的 Load-Exclusive 指令,参见 Load-Exclusive 和
Store-Exclusive 使用限制。
Blockquote
根据“Barrier Litmus Tests and Cookbook”文档,我用 C 编写了一个 claim_lock 函数。我检查了生成的代码,一切看起来都不错,但是没有用。
// This code conforms to the section 7.2 of PRD03-GENC-007826:
// "Acquiring and Releasing a Lock"
static inline void claim_lock( uint32_t volatile *lock )
{
uint32_t failed = 1;
uint32_t value;
while (failed) {
asm volatile ( "ldrex %[value], [%[lock]]"
: [value] "=&r" (value)
: [lock] "r" (lock) );
if (value == 0) {
// The failed and lock registers are not allowed to be the same, so
// pretend to gcc that the lock pointer may be written as well as read.
asm volatile ( "strex %[failed], %[value], [%[lock]]"
: [failed] "=&r" (failed)
, [lock] "+r" (lock)
: [value] "r" (1) );
}
else {
asm ( "clrex" );
}
}
asm ( "dmb sy" );
}
生成的代码 (gcc):
1000: e3a03001 mov r3, #1
1004: e1902f9f ldrex r2, [r0]
1008: e3520000 cmp r2, #0
100c: 1a000004 bne 1024 <claim_lock+0x24>
1010: e1802f93 strex r2, r3, [r0]
1014: e3520000 cmp r2, #0
1018: 1afffff9 bne 1004 <claim_lock+0x4>
101c: f57ff05f dmb sy
1020: e12fff1e bx lr
1024: f57ff01f clrex
1028: eafffff5 b 1004 <claim_lock+0x4>
对应的释放函数:
static inline void release_lock( uint32_t volatile *lock )
{
// Ensure that any changes made while holding the lock are
// visible before the lock is seen to have been released
asm ( "dmb sy" );
*lock = 0;
}
它在 QEMU 中工作,但要么挂起,要么允许所有内核在真实硬件(Raspberry Pi 3 Cortex-A53)上“声明”所谓的“锁定”。
如果:
LDREX指令会挂掉核心(除非我的测试没有报异常)- 未启用 MMU
- 包含锁的虚拟内存区域没有被缓存
如果出现以下情况,核心似乎会忽略彼此的主张:
- 尚未启用对称多处理
SMP 启用机制似乎因设备而异;检查特定内核的 TRM,它在 ARM ARM 的范围之外。
对于 Cortex-A53,要设置的位是 SMPEN,CPU 扩展控制寄存器的第 6 位,CPUECTLR。
较早的设备具有辅助控制寄存器的第 5 位,例如 (ARM11 MPcore),其中还需要考虑 SCU。我没有这样的设备,但正是我第一次注意到 SMP/nAMP 位的文档。
这是我在 ARMv7-M 架构的上下文切换部分找到的 参考手册
Blockquote
需要保证本地监视器在上下文切换后处于Open Access状态。在 ARMv7-M,本地监视器作为异常进入或退出的一部分自动更改为 Open Access 顺序。本地监视器也可以通过 CLREX 指令强制进入开放访问状态。 笔记 上下文切换不是应用程序级别的操作。但是,此信息包含在此处以 完成独占操作的描述。 上下文切换可能会导致后续 Store-Exclusive 失败,需要加载...存储序列 重播。为尽量减少发生这种情况的可能性,ARM 建议 Store-Exclusive 指令尽可能靠近关联的 Load-Exclusive 指令,参见 Load-Exclusive 和 Store-Exclusive 使用限制。
Blockquote