struct cpu_context {
unsigned long x19;
unsigned long x20;
unsigned long x21;
unsigned long x22;
unsigned long x23;
unsigned long x24;
unsigned long x25;
unsigned long x26;
unsigned long x27;
unsigned long x28;
unsigned long fp;
unsigned long sp;
unsigned long pc;
};
struct thread_struct {
struct cpu_context cpu_context; //保存非通用寄存器的值
unsigned long tp_value; //指向线程私有的内存区域
#ifdef CONFIG_COMPAT
unsigned long tp2_value;
#endif
struct fpsimd_state fpsimd_state;
unsigned long fault_address; /* fault info */
unsigned long fault_code; /* ESR_EL1 value */
struct debug_info debug; /* debugging */
};
arm switch_to定义如下:
#define switch_to(prev,next,last) \
do { \
__complete_pending_tlbi(); //暂时不去深究 \
last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \
} while (0)
__switch_to 函数定义
struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *next)
{
struct task_struct *last;
fpsimd_thread_switch(next); //保存fpsimd状态 task>thread.fpsimd_state,根据新的进程的状态设置cpu
tls_thread_switch(next); //线程局部变量地址切换
hw_breakpoint_thread_switch(next);
contextidr_thread_switch(next); //这两个是跟硬件跟踪相关,这里不深究
/*
* Complete any pending TLB or cache maintenance on this CPU in case
* the thread migrates to a different CPU.
*/
dsb(ish); //插入屏障
/* the actual thread switch */
last = cpu_switch_to(prev, next); //进行正在的切换动作,注意这里返回之后则是运行到了next进程的switch_to函数剩余部分,则是执行next进程,保存的pc地址也就是这里的return指令地址。
return last;
}
tls_thread_switch函数:
static void tls_thread_switch(struct task_struct *next)
{
unsigned long tpidr, tpidrro;
asm("mrs %0, tpidr_el0" : "=r" (tpidr)); //内联汇编,将tpidr_el0寄存器加载到tpidr变量中。
*task_user_tls(current) = tpidr; //这里是将上一步获取的tpidr_el0值保存到current->thread.tp_value
tpidr = *task_user_tls(next); //读取新的进程的next->thread.tp_value 到tpidr中
tpidrro = is_compat_thread(task_thread_info(next)) ?
next->thread.tp_value : 0; //这个是先判断是否存在TIF_32BIT标志,存在则将next->thread.tp_value也赋值到tpidrro变量
asm(
" msr tpidr_el0, %0\n" //这三句则是将上面读取到的tpidr、tpidrro里去。
" msr tpidrro_el0, %1"
: : "r" (tpidr), "r" (tpidrro));
}
tls_thread_switch函数则是保存旧的current->thread.tp_value加载新的next->thread.tp_value
cpu_switch_to函数:
ENTRY(cpu_switch_to)
//DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context));
mov x10, #THREAD_CPU_CONTEXT //从上述定义可以看出是thread.cpu_context相对于task的偏移
add x8, x0, x10 //这句汇编等价于x8=prev->thread.cpu_context
mov x9, sp //将sp保存到x9
stp x19, x20, [x8], #16 // 将x19 x20寄存器保存到prev->thread.cpu_context.x19 x18中
stp x21, x22, [x8], #16 //类似上述指令
stp x23, x24, [x8], #16
stp x25, x26, [x8], #16
stp x27, x28, [x8], #16
stp x29, x9, [x8], #16 //保存x29和sp
str lr, [x8] // 将lr寄存器保存到prev->thread.cpu_context.pc中
add x8, x1, x10 //这里x8=next->thread.cpu_context
ldp x19, x20, [x8], #16 // restore callee-saved registers
ldp x21, x22, [x8], #16
ldp x23, x24, [x8], #16
ldp x25, x26, [x8], #16
ldp x27, x28, [x8], #16
ldp x29, x9, [x8], #16
ldr lr, [x8]
mov sp, x9
ret //arm返回值都是保存在x0中所以这里返回的task地址还是prev的
ENDPROC(cpu_switch_to)
1、上述汇编指令将lr保存为pc值,是因为lr是switch_to函数的return指令,正常恢复执行流的函数都会执行到这里。
2、注意函数返回的task_struct结构体是prev指向的地址
3、上述可以看到switch_to只是做了硬件切换没有进行内存地址等的切换