mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-04-11 16:37:45 -04:00
arm: fix Cortex-M IRQ masking
The following inline assembly in set_irq_level() turned out
to have incorrect constraints:
int newvalue = /* input parameter */;
int oldvalue;
asm volatile ("mrs %0, primask\n"
"msr primask, %1\n"
: "=r"(oldvalue) : "r"(newvalue));
leading to incorrect code generation for common cases like
disable_irq_save(), which compiles to:
mov r5, #1
mrs r5, primask
msr primask, r5
...which doesn't disable IRQs at all, since both of the
operands got assigned to the same register; the write of
'oldvalue' clobbers the 'newvalue' input before it's used.
Apparently GCC assumes that input operands are read before
output operands are written. One way to fix this is adding
the '&' constraint: "=&r"(oldvalue), but it's better to
break things down into separate, simpler asm statements
which GCC can figure out itself.
Also add compiler memory barriers where primask is modified
to ensure loads/stores aren't incorrectly moved outside of
critical sections.
While here, optimize disable_irq_save() a bit by using the
cpsid instruction, which avoids the extra "mov" and register
allocation needed by "msr primask".
Change-Id: Iac94a76db5bac399a1cf028da4241a0473259a46
This commit is contained in:
parent
0474dca7c3
commit
5442622d88
1 changed files with 35 additions and 26 deletions
|
|
@ -26,9 +26,6 @@
|
|||
#define IRQ_STATUS 0x01
|
||||
#define HIGHEST_IRQ_LEVEL IRQ_DISABLED
|
||||
|
||||
#define disable_irq_save() \
|
||||
set_irq_level(IRQ_DISABLED)
|
||||
|
||||
/* For compatibility with ARM classic */
|
||||
#define CPU_MODE_THREAD_CONTEXT 0
|
||||
|
||||
|
|
@ -47,39 +44,51 @@
|
|||
__func__, __mproc, __massert); })
|
||||
|
||||
/* Core-level interrupt masking */
|
||||
|
||||
static inline int set_irq_level(int primask)
|
||||
{
|
||||
int oldvalue;
|
||||
|
||||
asm volatile ("mrs %0, primask\n"
|
||||
"msr primask, %1\n"
|
||||
: "=r"(oldvalue) : "r"(primask));
|
||||
|
||||
return oldvalue;
|
||||
}
|
||||
|
||||
static inline void restore_irq(int primask)
|
||||
{
|
||||
asm volatile ("msr primask, %0" :: "r"(primask));
|
||||
}
|
||||
|
||||
static inline void enable_irq(void)
|
||||
{
|
||||
asm volatile ("cpsie i");
|
||||
asm volatile ("cpsie i" ::: "memory");
|
||||
}
|
||||
|
||||
static inline void disable_irq(void)
|
||||
{
|
||||
asm volatile ("cpsid i");
|
||||
asm volatile ("cpsid i" ::: "memory");
|
||||
}
|
||||
|
||||
static inline void restore_irq(int primask)
|
||||
{
|
||||
asm volatile ("msr primask, %0" :: "r"(primask) : "memory");
|
||||
}
|
||||
|
||||
static inline int get_irq_level(void)
|
||||
{
|
||||
int primask;
|
||||
|
||||
asm volatile("mrs %0, primask" : "=r"(primask));
|
||||
|
||||
return primask;
|
||||
}
|
||||
|
||||
static inline int disable_irq_save(void)
|
||||
{
|
||||
int oldlevel = get_irq_level();
|
||||
|
||||
disable_irq();
|
||||
|
||||
return oldlevel;
|
||||
}
|
||||
|
||||
static inline int set_irq_level(int primask)
|
||||
{
|
||||
int oldvalue = get_irq_level();
|
||||
|
||||
restore_irq(primask);
|
||||
|
||||
return oldvalue;
|
||||
}
|
||||
|
||||
static inline bool irq_enabled(void)
|
||||
{
|
||||
int primask;
|
||||
asm volatile ("mrs %0, primask" : "=r"(primask));
|
||||
|
||||
return !(primask & 1);
|
||||
return get_irq_level() == IRQ_ENABLED;
|
||||
}
|
||||
|
||||
static inline unsigned long get_interrupt_number(void)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue