【发布时间】:2018-08-08 15:29:28
【问题描述】:
我正在尝试在用户空间和内核空间之间的边界添加一些逻辑,特别是在 ARM 架构上。
一个这样的边界似乎是在arch/arm/kernel/entry-common.S 中实现的vector_swi 例程。现在,我的大部分代码都是用 C 函数编写的,我想在 vector_swi 开头的某个地方调用它。
因此,我做了以下事情:
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0 - r12
ARM( add r8, sp, #S_PC )
ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr
THUMB( mov r8, sp )
THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr
mrs r8, spsr @ called from non-FIQ mode, so ok.
str lr, [sp, #S_PC] @ Save calling PC
str r8, [sp, #S_PSR] @ Save CPSR
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
zero_fp
#ifdef CONFIG_BTM_BOUNDARIES
bl btm_entering_kernelspace @ <--- My function
#endif
当我的函数内容如下时,一切正常:
static int btm_enabled = 0;
asmlinkage inline void btm_entering_kernelspace(void)
{
int cpu;
int freq;
struct acpu_level *level;
if(!btm_enabled) {
return;
}
cpu = smp_processor_id();
freq = acpuclk_krait_get_rate(cpu);
(void) cpu;
(void) freq;
(void) level;
}
但是,当我添加一些额外的代码时,内核会进入崩溃重启循环。
static int btm_enabled = 0;
asmlinkage inline void btm_entering_kernelspace(void)
{
int cpu;
int freq;
struct acpu_level *level;
if(!btm_enabled) {
return;
}
cpu = smp_processor_id();
freq = acpuclk_krait_get_rate(cpu);
(void) cpu;
(void) freq;
(void) level;
// --------- Added code ----------
for (level = drv.acpu_freq_tbl; level->speed.khz != 0; level++) {
if(level->speed.khz == freq) {
break;
}
}
}
虽然第一直觉是责怪添加代码的逻辑,但请注意,由于btm_enabled 是0,因此任何代码都不应该执行。
我通过添加一个sysfs 条目来打印出变量的值(删除了添加的代码),双重检查和三重检查以确保btm_enabled 是0。
有人可以解释这里发生了什么或我做错了什么吗?
【问题讨论】:
-
新代码可能会更改早期代码的寄存器分配,和/或编译器可能决定在检查/返回之前乐观地对寄存器做一些事情。或者只是将一些寄存器保存/恢复到堆栈,因为该函数现在需要在调用之间保存一些参数。如果 clobbing r0..r3 和堆栈指针下方的内存在
vector_swi中当时并不安全,那么带有函数调用的第一个版本只是偶然的。我对 Linux 的 ARM 入口点不够熟悉,无法说明 ABI 兼容的函数调用在那里是否安全。
标签: c assembly linux-kernel arm